mirror of
https://github.com/cake-tech/cake_wallet.git
synced 2025-06-28 20:39:51 +00:00
Merge branch 'main' into CW-1063-New-Contacts-Address-book-Flow
This commit is contained in:
commit
39de76b11c
191 changed files with 5871 additions and 1260 deletions
21
.github/workflows/no_http_imports.yaml
vendored
Normal file
21
.github/workflows/no_http_imports.yaml
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
name: No http imports
|
||||||
|
|
||||||
|
on: [pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
PR_test_build:
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Check for http package usage
|
||||||
|
if: github.event_name == 'pull_request'
|
||||||
|
run: |
|
||||||
|
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)"
|
||||||
|
[[ "x$GIT_GREP_OUT" == "x" ]] && exit 0
|
||||||
|
echo "$GIT_GREP_OUT"
|
||||||
|
echo "There are .dart files which use http imports"
|
||||||
|
echo "Using http package breaks proxy integration"
|
||||||
|
echo "Please use ProxyWrapper.getHttpClient() from package:cw_core/utils/proxy_wrapper.dart"
|
||||||
|
exit 1
|
||||||
|
|
2
.github/workflows/no_print_in_dart.yaml
vendored
2
.github/workflows/no_print_in_dart.yaml
vendored
|
@ -15,5 +15,5 @@ jobs:
|
||||||
[[ "x$GIT_GREP_OUT" == "x" ]] && exit 0
|
[[ "x$GIT_GREP_OUT" == "x" ]] && exit 0
|
||||||
echo "$GIT_GREP_OUT"
|
echo "$GIT_GREP_OUT"
|
||||||
echo "There are .dart files which use print() statements"
|
echo "There are .dart files which use print() statements"
|
||||||
echo "Please use printV from package: cw_core/utils/print_verbose.dart"
|
echo "Please use printV from package:cw_core/utils/print_verbose.dart"
|
||||||
exit 1
|
exit 1
|
||||||
|
|
BIN
assets/images/qr-cake.png
Normal file
BIN
assets/images/qr-cake.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 72 KiB |
76
assets/images/tor_logo.svg
Normal file
76
assets/images/tor_logo.svg
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 17.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<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"
|
||||||
|
viewBox="0 0 150.5 91.1" enable-background="new 0 0 150.5 91.1" xml:space="preserve">
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<g id="layer3_22_" transform="translate(-92,-63.999774)">
|
||||||
|
<g id="layer5_22_">
|
||||||
|
<g id="path2554_38_">
|
||||||
|
<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
|
||||||
|
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"/>
|
||||||
|
</g>
|
||||||
|
<g id="path2534_13_">
|
||||||
|
<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
|
||||||
|
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"/>
|
||||||
|
</g>
|
||||||
|
<g id="path2536_23_">
|
||||||
|
<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
|
||||||
|
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"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g id="layer4_22_">
|
||||||
|
<g id="path2540_23_">
|
||||||
|
<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
|
||||||
|
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
|
||||||
|
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
|
||||||
|
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"/>
|
||||||
|
</g>
|
||||||
|
<g id="path2542_23_">
|
||||||
|
<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
|
||||||
|
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
|
||||||
|
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
|
||||||
|
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
|
||||||
|
C173.1,120.8,173.1,118,172.7,114.8z"/>
|
||||||
|
</g>
|
||||||
|
<g id="path2544_23_">
|
||||||
|
<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
|
||||||
|
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
|
||||||
|
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
|
||||||
|
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
|
||||||
|
C173.7,133.8,172.6,130.7,172.1,128.5L172.1,128.5z"/>
|
||||||
|
</g>
|
||||||
|
<g id="path2550_23_">
|
||||||
|
<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
|
||||||
|
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
|
||||||
|
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"/>
|
||||||
|
</g>
|
||||||
|
<g id="path2552_23_">
|
||||||
|
<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
|
||||||
|
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
|
||||||
|
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"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g id="path2528_18_">
|
||||||
|
<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
|
||||||
|
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
|
||||||
|
C0,10.5,1.7,8.8,3.8,8.8z"/>
|
||||||
|
</g>
|
||||||
|
<g id="path2532_18_">
|
||||||
|
<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
|
||||||
|
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
|
||||||
|
C140.5,30.1,141.9,30,142.6,30L142.6,30z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<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
|
||||||
|
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
|
||||||
|
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
|
||||||
|
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
|
||||||
|
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
|
||||||
|
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"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 4.8 KiB |
|
@ -266,6 +266,12 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
||||||
derivationPath: walletInfo.derivationInfo!.derivationPath!);
|
derivationPath: walletInfo.derivationInfo!.derivationPath!);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> close({bool shouldCleanup = false}) async {
|
||||||
|
payjoinManager.cleanupSessions();
|
||||||
|
super.close(shouldCleanup: shouldCleanup);
|
||||||
|
}
|
||||||
|
|
||||||
late final PayjoinManager payjoinManager;
|
late final PayjoinManager payjoinManager;
|
||||||
|
|
||||||
bool get isPayjoinAvailable => unspentCoinsInfo.values
|
bool get isPayjoinAvailable => unspentCoinsInfo.values
|
||||||
|
|
|
@ -59,19 +59,26 @@ abstract class BitcoinWalletAddressesBase extends ElectrumWalletAddresses with S
|
||||||
|
|
||||||
@action
|
@action
|
||||||
Future<void> initPayjoin() async {
|
Future<void> initPayjoin() async {
|
||||||
await payjoinManager.initPayjoin();
|
try {
|
||||||
currentPayjoinReceiver = await payjoinManager.initReceiver(primaryAddress);
|
await payjoinManager.initPayjoin();
|
||||||
payjoinEndpoint = (await currentPayjoinReceiver?.pjUri())?.pjEndpoint();
|
currentPayjoinReceiver = await payjoinManager.getUnusedReceiver(primaryAddress);
|
||||||
|
payjoinEndpoint = (await currentPayjoinReceiver?.pjUri())?.pjEndpoint();
|
||||||
|
|
||||||
payjoinManager.resumeSessions();
|
payjoinManager.resumeSessions();
|
||||||
|
} catch (e) {
|
||||||
|
printV(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
Future<void> newPayjoinReceiver() async {
|
Future<void> newPayjoinReceiver() async {
|
||||||
currentPayjoinReceiver = await payjoinManager.initReceiver(primaryAddress);
|
try {
|
||||||
payjoinEndpoint = (await currentPayjoinReceiver?.pjUri())?.pjEndpoint();
|
currentPayjoinReceiver = await payjoinManager.getUnusedReceiver(primaryAddress);
|
||||||
|
payjoinEndpoint = (await currentPayjoinReceiver?.pjUri())?.pjEndpoint();
|
||||||
|
|
||||||
printV("Initializing new Payjoin Receiver");
|
payjoinManager.spawnReceiver(receiver: currentPayjoinReceiver!);
|
||||||
payjoinManager.spawnNewReceiver(receiver: currentPayjoinReceiver!);
|
} catch (e) {
|
||||||
|
printV(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,8 @@ import 'dart:typed_data';
|
||||||
import 'package:bitcoin_base/bitcoin_base.dart';
|
import 'package:bitcoin_base/bitcoin_base.dart';
|
||||||
import 'package:cw_bitcoin/bitcoin_amount_format.dart';
|
import 'package:cw_bitcoin/bitcoin_amount_format.dart';
|
||||||
import 'package:cw_core/utils/print_verbose.dart';
|
import 'package:cw_core/utils/print_verbose.dart';
|
||||||
|
import 'package:cw_core/utils/proxy_socket/abstract.dart';
|
||||||
|
import 'package:cw_core/utils/proxy_wrapper.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:rxdart/rxdart.dart';
|
import 'package:rxdart/rxdart.dart';
|
||||||
|
|
||||||
|
@ -42,7 +44,7 @@ class ElectrumClient {
|
||||||
static const aliveTimerDuration = Duration(seconds: 4);
|
static const aliveTimerDuration = Duration(seconds: 4);
|
||||||
|
|
||||||
bool get isConnected => _isConnected;
|
bool get isConnected => _isConnected;
|
||||||
Socket? socket;
|
ProxySocket? socket;
|
||||||
void Function(ConnectionStatus)? onConnectionStatusChange;
|
void Function(ConnectionStatus)? onConnectionStatusChange;
|
||||||
int _id;
|
int _id;
|
||||||
final Map<String, SocketTask> _tasks;
|
final Map<String, SocketTask> _tasks;
|
||||||
|
@ -72,18 +74,11 @@ class ElectrumClient {
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
socket = null;
|
socket = null;
|
||||||
|
|
||||||
|
final ssl = !(useSSL == false || (useSSL == null && uri.toString().contains("btc-electrum")));
|
||||||
try {
|
try {
|
||||||
if (useSSL == false || (useSSL == null && uri.toString().contains("btc-electrum"))) {
|
socket = await ProxyWrapper().getSocksSocket(ssl, host, port, connectionTimeout: connectionTimeout);
|
||||||
socket = await Socket.connect(host, port, timeout: connectionTimeout);
|
|
||||||
} else {
|
|
||||||
socket = await SecureSocket.connect(
|
|
||||||
host,
|
|
||||||
port,
|
|
||||||
timeout: connectionTimeout,
|
|
||||||
onBadCertificate: (_) => true,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
printV("connect: $e");
|
||||||
if (e is HandshakeException) {
|
if (e is HandshakeException) {
|
||||||
useSSL = !(useSSL ?? false);
|
useSSL = !(useSSL ?? false);
|
||||||
}
|
}
|
||||||
|
@ -105,7 +100,6 @@ class ElectrumClient {
|
||||||
|
|
||||||
// use ping to determine actual connection status since we could've just not timed out yet:
|
// use ping to determine actual connection status since we could've just not timed out yet:
|
||||||
// _setConnectionStatus(ConnectionStatus.connected);
|
// _setConnectionStatus(ConnectionStatus.connected);
|
||||||
|
|
||||||
socket!.listen(
|
socket!.listen(
|
||||||
(Uint8List event) {
|
(Uint8List event) {
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -4,6 +4,7 @@ import 'dart:io';
|
||||||
import 'dart:isolate';
|
import 'dart:isolate';
|
||||||
|
|
||||||
import 'package:bitcoin_base/bitcoin_base.dart';
|
import 'package:bitcoin_base/bitcoin_base.dart';
|
||||||
|
import 'package:cw_core/utils/proxy_wrapper.dart';
|
||||||
import 'package:cw_bitcoin/bitcoin_amount_format.dart';
|
import 'package:cw_bitcoin/bitcoin_amount_format.dart';
|
||||||
import 'package:cw_core/format_amount.dart';
|
import 'package:cw_core/format_amount.dart';
|
||||||
import 'package:cw_core/utils/print_verbose.dart';
|
import 'package:cw_core/utils/print_verbose.dart';
|
||||||
|
@ -49,7 +50,6 @@ import 'package:mobx/mobx.dart';
|
||||||
import 'package:rxdart/subjects.dart';
|
import 'package:rxdart/subjects.dart';
|
||||||
import 'package:sp_scanner/sp_scanner.dart';
|
import 'package:sp_scanner/sp_scanner.dart';
|
||||||
import 'package:hex/hex.dart';
|
import 'package:hex/hex.dart';
|
||||||
import 'package:http/http.dart' as http;
|
|
||||||
|
|
||||||
part 'electrum_wallet.g.dart';
|
part 'electrum_wallet.g.dart';
|
||||||
|
|
||||||
|
@ -493,10 +493,9 @@ abstract class ElectrumWalletBase
|
||||||
Future<void> updateFeeRates() async {
|
Future<void> updateFeeRates() async {
|
||||||
if (await checkIfMempoolAPIIsEnabled() && type == WalletType.bitcoin) {
|
if (await checkIfMempoolAPIIsEnabled() && type == WalletType.bitcoin) {
|
||||||
try {
|
try {
|
||||||
final response = await http
|
final response = await ProxyWrapper()
|
||||||
.get(Uri.parse("https://mempool.cakewallet.com/api/v1/fees/recommended"))
|
.get(clearnetUri: Uri.parse("https://mempool.cakewallet.com/api/v1/fees/recommended"))
|
||||||
.timeout(Duration(seconds: 5));
|
.timeout(Duration(seconds: 15));
|
||||||
|
|
||||||
final result = json.decode(response.body) as Map<String, dynamic>;
|
final result = json.decode(response.body) as Map<String, dynamic>;
|
||||||
final slowFee = (result['economyFee'] as num?)?.toInt() ?? 0;
|
final slowFee = (result['economyFee'] as num?)?.toInt() ?? 0;
|
||||||
int mediumFee = (result['hourFee'] as num?)?.toInt() ?? 0;
|
int mediumFee = (result['hourFee'] as num?)?.toInt() ?? 0;
|
||||||
|
@ -1176,20 +1175,18 @@ abstract class ElectrumWalletBase
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return PendingBitcoinTransaction(
|
return PendingBitcoinTransaction(transaction, type,
|
||||||
transaction,
|
electrumClient: electrumClient,
|
||||||
type,
|
amount: estimatedTx.amount,
|
||||||
electrumClient: electrumClient,
|
fee: estimatedTx.fee,
|
||||||
amount: estimatedTx.amount,
|
feeRate: feeRateInt.toString(),
|
||||||
fee: estimatedTx.fee,
|
network: network,
|
||||||
feeRate: feeRateInt.toString(),
|
hasChange: estimatedTx.hasChange,
|
||||||
network: network,
|
isSendAll: estimatedTx.isSendAll,
|
||||||
hasChange: estimatedTx.hasChange,
|
hasTaprootInputs: hasTaprootInputs,
|
||||||
isSendAll: estimatedTx.isSendAll,
|
utxos: estimatedTx.utxos,
|
||||||
hasTaprootInputs: hasTaprootInputs,
|
publicKeys: estimatedTx.publicKeys)
|
||||||
utxos: estimatedTx.utxos,
|
..addListener((transaction) async {
|
||||||
publicKeys: estimatedTx.publicKeys
|
|
||||||
)..addListener((transaction) async {
|
|
||||||
transactionHistory.addOne(transaction);
|
transactionHistory.addOne(transaction);
|
||||||
if (estimatedTx.spendsSilentPayment) {
|
if (estimatedTx.spendsSilentPayment) {
|
||||||
transactionHistory.transactions.values.forEach((tx) {
|
transactionHistory.transactions.values.forEach((tx) {
|
||||||
|
@ -1880,20 +1877,17 @@ abstract class ElectrumWalletBase
|
||||||
|
|
||||||
if (height != null && height > 0 && await checkIfMempoolAPIIsEnabled()) {
|
if (height != null && height > 0 && await checkIfMempoolAPIIsEnabled()) {
|
||||||
try {
|
try {
|
||||||
final blockHash = await http.get(
|
final blockHash = await ProxyWrapper()
|
||||||
Uri.parse(
|
.get(clearnetUri: Uri.parse("https://mempool.cakewallet.com/api/v1/block-height/$height"))
|
||||||
"https://mempool.cakewallet.com/api/v1/block-height/$height",
|
.timeout(Duration(seconds: 15));
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
if (blockHash.statusCode == 200 &&
|
if (blockHash.statusCode == 200 &&
|
||||||
blockHash.body.isNotEmpty &&
|
blockHash.body.isNotEmpty &&
|
||||||
jsonDecode(blockHash.body) != null) {
|
jsonDecode(blockHash.body) != null) {
|
||||||
final blockResponse = await http.get(
|
final blockResponse = await ProxyWrapper()
|
||||||
Uri.parse(
|
.get(clearnetUri: Uri.parse("https://mempool.cakewallet.com/api/v1/block/${blockHash}"))
|
||||||
"https://mempool.cakewallet.com/api/v1/block/${blockHash.body}",
|
.timeout(Duration(seconds: 15));
|
||||||
),
|
|
||||||
);
|
|
||||||
if (blockResponse.statusCode == 200 &&
|
if (blockResponse.statusCode == 200 &&
|
||||||
blockResponse.body.isNotEmpty &&
|
blockResponse.body.isNotEmpty &&
|
||||||
jsonDecode(blockResponse.body)['timestamp'] != null) {
|
jsonDecode(blockResponse.body)['timestamp'] != null) {
|
||||||
|
|
|
@ -53,7 +53,7 @@ class PayjoinManager {
|
||||||
}
|
}
|
||||||
final receiver = Receiver.fromJson(json: session.receiver!);
|
final receiver = Receiver.fromJson(json: session.receiver!);
|
||||||
printV("Resuming Payjoin Receiver Session ${receiver.id()}");
|
printV("Resuming Payjoin Receiver Session ${receiver.id()}");
|
||||||
return _spawnReceiver(receiver: receiver);
|
return spawnReceiver(receiver: receiver);
|
||||||
});
|
});
|
||||||
|
|
||||||
printV("Resumed ${spawnedSessions.length} Payjoin Sessions");
|
printV("Resumed ${spawnedSessions.length} Payjoin Sessions");
|
||||||
|
@ -121,15 +121,13 @@ class PayjoinManager {
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
_cleanupSession(pjUri);
|
_cleanupSession(pjUri);
|
||||||
printV(e);
|
await _payjoinStorage.markSenderSessionUnrecoverable(pjUri, e.toString());
|
||||||
await _payjoinStorage.markSenderSessionUnrecoverable(pjUri);
|
completer.complete();
|
||||||
completer.completeError(e);
|
|
||||||
}
|
}
|
||||||
} else if (message is PayjoinSessionError) {
|
} else if (message is PayjoinSessionError) {
|
||||||
_cleanupSession(pjUri);
|
_cleanupSession(pjUri);
|
||||||
if (message is UnrecoverableError) {
|
if (message is UnrecoverableError) {
|
||||||
printV(message.message);
|
await _payjoinStorage.markSenderSessionUnrecoverable(pjUri, message.message);
|
||||||
await _payjoinStorage.markSenderSessionUnrecoverable(pjUri);
|
|
||||||
completer.complete();
|
completer.complete();
|
||||||
} else if (message is RecoverableError) {
|
} else if (message is RecoverableError) {
|
||||||
completer.complete();
|
completer.complete();
|
||||||
|
@ -149,42 +147,41 @@ class PayjoinManager {
|
||||||
return completer.future;
|
return completer.future;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Receiver> initReceiver(String address,
|
Future<Receiver> getUnusedReceiver(String address,
|
||||||
[bool isTestnet = false]) async {
|
[bool isTestnet = false]) async {
|
||||||
try {
|
final session = _payjoinStorage.getUnusedActiveReceiverSession(_wallet.id);
|
||||||
final ohttpKeys = await PayjoinUri.fetchOhttpKeys(
|
|
||||||
ohttpRelay: await randomOhttpRelayUrl(),
|
|
||||||
payjoinDirectory: payjoinDirectoryUrl,
|
|
||||||
);
|
|
||||||
|
|
||||||
final newReceiver = await NewReceiver.create(
|
if (session != null) {
|
||||||
address: address,
|
await PayjoinUri.Url.fromStr(payjoinDirectoryUrl);
|
||||||
network: isTestnet ? Network.testnet : Network.bitcoin,
|
|
||||||
directory: payjoinDirectoryUrl,
|
|
||||||
ohttpKeys: ohttpKeys,
|
|
||||||
);
|
|
||||||
final persister = PayjoinReceiverPersister.impl();
|
|
||||||
final receiverToken = await newReceiver.persist(persister: persister);
|
|
||||||
final receiver =
|
|
||||||
await Receiver.load(persister: persister, token: receiverToken);
|
|
||||||
|
|
||||||
await _payjoinStorage.insertReceiverSession(receiver, _wallet.id);
|
return Receiver.fromJson(json: session.receiver!);
|
||||||
|
|
||||||
return receiver;
|
|
||||||
} catch (e) {
|
|
||||||
throw Exception('Error initializing Payjoin Receiver: $e');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return initReceiver(address);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> spawnNewReceiver({
|
Future<Receiver> initReceiver(String address, [bool isTestnet = false]) async {
|
||||||
required Receiver receiver,
|
final ohttpKeys = await PayjoinUri.fetchOhttpKeys(
|
||||||
bool isTestnet = false,
|
ohttpRelay: await randomOhttpRelayUrl(),
|
||||||
}) async {
|
payjoinDirectory: payjoinDirectoryUrl,
|
||||||
|
);
|
||||||
|
|
||||||
|
final newReceiver = await NewReceiver.create(
|
||||||
|
address: address,
|
||||||
|
network: isTestnet ? Network.testnet : Network.bitcoin,
|
||||||
|
directory: payjoinDirectoryUrl,
|
||||||
|
ohttpKeys: ohttpKeys,
|
||||||
|
);
|
||||||
|
final persister = PayjoinReceiverPersister.impl();
|
||||||
|
final receiverToken = await newReceiver.persist(persister: persister);
|
||||||
|
final receiver = await Receiver.load(persister: persister, token: receiverToken);
|
||||||
|
|
||||||
await _payjoinStorage.insertReceiverSession(receiver, _wallet.id);
|
await _payjoinStorage.insertReceiverSession(receiver, _wallet.id);
|
||||||
return _spawnReceiver(isTestnet: isTestnet, receiver: receiver);
|
|
||||||
|
return receiver;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _spawnReceiver({
|
Future<void> spawnReceiver({
|
||||||
required Receiver receiver,
|
required Receiver receiver,
|
||||||
bool isTestnet = false,
|
bool isTestnet = false,
|
||||||
}) async {
|
}) async {
|
||||||
|
@ -229,6 +226,10 @@ class PayjoinManager {
|
||||||
|
|
||||||
case PayjoinReceiverRequestTypes.getCandidateInputs:
|
case PayjoinReceiverRequestTypes.getCandidateInputs:
|
||||||
utxos = _wallet.getUtxoWithPrivateKeys();
|
utxos = _wallet.getUtxoWithPrivateKeys();
|
||||||
|
if (utxos.isEmpty) {
|
||||||
|
await _wallet.updateAllUnspents();
|
||||||
|
utxos = _wallet.getUtxoWithPrivateKeys();
|
||||||
|
}
|
||||||
mainToIsolateSendPort?.send({
|
mainToIsolateSendPort?.send({
|
||||||
'requestId': message['requestId'],
|
'requestId': message['requestId'],
|
||||||
'result': utxos,
|
'result': utxos,
|
||||||
|
|
|
@ -8,11 +8,12 @@ import 'package:cw_bitcoin/payjoin/manager.dart';
|
||||||
import 'package:cw_bitcoin/payjoin/payjoin_session_errors.dart';
|
import 'package:cw_bitcoin/payjoin/payjoin_session_errors.dart';
|
||||||
import 'package:cw_bitcoin/psbt/signer.dart';
|
import 'package:cw_bitcoin/psbt/signer.dart';
|
||||||
import 'package:cw_core/utils/print_verbose.dart';
|
import 'package:cw_core/utils/print_verbose.dart';
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:cw_core/utils/proxy_wrapper.dart';
|
||||||
import 'package:payjoin_flutter/bitcoin_ffi.dart';
|
import 'package:payjoin_flutter/bitcoin_ffi.dart';
|
||||||
import 'package:payjoin_flutter/common.dart';
|
import 'package:payjoin_flutter/common.dart';
|
||||||
import 'package:payjoin_flutter/receive.dart';
|
import 'package:payjoin_flutter/receive.dart';
|
||||||
import 'package:payjoin_flutter/src/generated/frb_generated.dart' as pj;
|
import 'package:payjoin_flutter/src/generated/frb_generated.dart' as pj;
|
||||||
|
import 'package:http/http.dart' as very_insecure_http_do_not_use; // for errors
|
||||||
|
|
||||||
enum PayjoinReceiverRequestTypes {
|
enum PayjoinReceiverRequestTypes {
|
||||||
processOriginalTx,
|
processOriginalTx,
|
||||||
|
@ -28,7 +29,7 @@ class PayjoinReceiverWorker {
|
||||||
final pendingRequests = <String, Completer<dynamic>>{};
|
final pendingRequests = <String, Completer<dynamic>>{};
|
||||||
|
|
||||||
PayjoinReceiverWorker._(this.sendPort);
|
PayjoinReceiverWorker._(this.sendPort);
|
||||||
|
static final client = ProxyWrapper().getHttpIOClient();
|
||||||
static Future<void> run(List<Object> args) async {
|
static Future<void> run(List<Object> args) async {
|
||||||
await pj.core.init();
|
await pj.core.init();
|
||||||
|
|
||||||
|
@ -42,11 +43,10 @@ class PayjoinReceiverWorker {
|
||||||
receivePort.listen(worker.handleMessage);
|
receivePort.listen(worker.handleMessage);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final httpClient = http.Client();
|
|
||||||
final receiver = Receiver.fromJson(json: receiverJson);
|
final receiver = Receiver.fromJson(json: receiverJson);
|
||||||
|
|
||||||
final uncheckedProposal =
|
final uncheckedProposal =
|
||||||
await worker.receiveUncheckedProposal(httpClient, receiver);
|
await worker.receiveUncheckedProposal(receiver);
|
||||||
|
|
||||||
final originalTx = await uncheckedProposal.extractTxToScheduleBroadcast();
|
final originalTx = await uncheckedProposal.extractTxToScheduleBroadcast();
|
||||||
sendPort.send({
|
sendPort.send({
|
||||||
|
@ -57,14 +57,14 @@ class PayjoinReceiverWorker {
|
||||||
final payjoinProposal = await worker.processPayjoinProposal(
|
final payjoinProposal = await worker.processPayjoinProposal(
|
||||||
uncheckedProposal,
|
uncheckedProposal,
|
||||||
);
|
);
|
||||||
final psbt = await worker.sendFinalProposal(httpClient, payjoinProposal);
|
final psbt = await worker.sendFinalProposal(payjoinProposal);
|
||||||
sendPort.send({
|
sendPort.send({
|
||||||
'type': PayjoinReceiverRequestTypes.proposalSent,
|
'type': PayjoinReceiverRequestTypes.proposalSent,
|
||||||
'psbt': psbt,
|
'psbt': psbt,
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e is HttpException ||
|
if (e is HttpException ||
|
||||||
(e is http.ClientException &&
|
(e is very_insecure_http_do_not_use.ClientException &&
|
||||||
e.message.contains("Software caused connection abort"))) {
|
e.message.contains("Software caused connection abort"))) {
|
||||||
sendPort.send(PayjoinSessionError.recoverable(e.toString()));
|
sendPort.send(PayjoinSessionError.recoverable(e.toString()));
|
||||||
} else {
|
} else {
|
||||||
|
@ -98,16 +98,16 @@ class PayjoinReceiverWorker {
|
||||||
return completer.future;
|
return completer.future;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<UncheckedProposal> receiveUncheckedProposal(
|
Future<UncheckedProposal> receiveUncheckedProposal(Receiver session) async {
|
||||||
http.Client httpClient, Receiver session) async {
|
|
||||||
while (true) {
|
while (true) {
|
||||||
printV("Polling for Proposal (${session.id()})");
|
printV("Polling for Proposal (${session.id()})");
|
||||||
final extractReq = await session.extractReq(
|
final extractReq = await session.extractReq(
|
||||||
ohttpRelay: PayjoinManager.randomOhttpRelayUrl());
|
ohttpRelay: await PayjoinManager.randomOhttpRelayUrl(),
|
||||||
|
);
|
||||||
final request = extractReq.$1;
|
final request = extractReq.$1;
|
||||||
|
|
||||||
final url = Uri.parse(request.url.asString());
|
final url = Uri.parse(request.url.asString());
|
||||||
final httpRequest = await httpClient.post(url,
|
final httpRequest = await client.post(url,
|
||||||
headers: {'Content-Type': request.contentType}, body: request.body);
|
headers: {'Content-Type': request.contentType}, body: request.body);
|
||||||
|
|
||||||
final proposal = await session.processRes(
|
final proposal = await session.processRes(
|
||||||
|
@ -116,14 +116,14 @@ class PayjoinReceiverWorker {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<String> sendFinalProposal(
|
Future<String> sendFinalProposal(PayjoinProposal finalProposal) async {
|
||||||
http.Client httpClient, PayjoinProposal finalProposal) async {
|
|
||||||
final req = await finalProposal.extractReq(
|
final req = await finalProposal.extractReq(
|
||||||
ohttpRelay: PayjoinManager.randomOhttpRelayUrl());
|
ohttpRelay: await PayjoinManager.randomOhttpRelayUrl(),
|
||||||
|
);
|
||||||
final proposalReq = req.$1;
|
final proposalReq = req.$1;
|
||||||
final proposalCtx = req.$2;
|
final proposalCtx = req.$2;
|
||||||
|
|
||||||
final request = await httpClient.post(
|
final request = await client.post(
|
||||||
Uri.parse(proposalReq.url.asString()),
|
Uri.parse(proposalReq.url.asString()),
|
||||||
headers: {"Content-Type": proposalReq.contentType},
|
headers: {"Content-Type": proposalReq.contentType},
|
||||||
body: proposalReq.body,
|
body: proposalReq.body,
|
||||||
|
@ -174,7 +174,7 @@ class PayjoinReceiverWorker {
|
||||||
final listUnspent =
|
final listUnspent =
|
||||||
await _sendRequest(PayjoinReceiverRequestTypes.getCandidateInputs);
|
await _sendRequest(PayjoinReceiverRequestTypes.getCandidateInputs);
|
||||||
final unspent = listUnspent as List<UtxoWithPrivateKey>;
|
final unspent = listUnspent as List<UtxoWithPrivateKey>;
|
||||||
if (unspent.isEmpty) throw Exception('No unspent outputs available');
|
if (unspent.isEmpty) throw RecoverableError('No unspent outputs available');
|
||||||
|
|
||||||
final selectedUtxo = await _inputPairFromUtxo(unspent[0]);
|
final selectedUtxo = await _inputPairFromUtxo(unspent[0]);
|
||||||
final pj6 = await pj5.contributeInputs(replacementInputs: [selectedUtxo]);
|
final pj6 = await pj5.contributeInputs(replacementInputs: [selectedUtxo]);
|
||||||
|
|
|
@ -5,7 +5,7 @@ import 'dart:isolate';
|
||||||
import 'package:cw_bitcoin/payjoin/manager.dart';
|
import 'package:cw_bitcoin/payjoin/manager.dart';
|
||||||
import 'package:cw_bitcoin/payjoin/payjoin_session_errors.dart';
|
import 'package:cw_bitcoin/payjoin/payjoin_session_errors.dart';
|
||||||
import 'package:cw_core/utils/print_verbose.dart';
|
import 'package:cw_core/utils/print_verbose.dart';
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:cw_core/utils/proxy_wrapper.dart';
|
||||||
import 'package:payjoin_flutter/common.dart';
|
import 'package:payjoin_flutter/common.dart';
|
||||||
import 'package:payjoin_flutter/send.dart';
|
import 'package:payjoin_flutter/send.dart';
|
||||||
import 'package:payjoin_flutter/src/generated/frb_generated.dart' as pj;
|
import 'package:payjoin_flutter/src/generated/frb_generated.dart' as pj;
|
||||||
|
@ -44,17 +44,17 @@ class PayjoinSenderWorker {
|
||||||
sendPort.send(e);
|
sendPort.send(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
final client = ProxyWrapper().getHttpIOClient();
|
||||||
|
|
||||||
/// Run a payjoin sender (V2 protocol first, fallback to V1).
|
/// Run a payjoin sender (V2 protocol first, fallback to V1).
|
||||||
Future<String> runSender(Sender sender) async {
|
Future<String> runSender(Sender sender) async {
|
||||||
final httpClient = http.Client();
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return await _runSenderV2(sender, httpClient);
|
return await _runSenderV2(sender);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
printV(e);
|
printV(e);
|
||||||
if (e is pj_error.FfiCreateRequestError) {
|
if (e is pj_error.FfiCreateRequestError) {
|
||||||
return await _runSenderV1(sender, httpClient);
|
return await _runSenderV1(sender);
|
||||||
} else if (e is HttpException) {
|
} else if (e is HttpException) {
|
||||||
printV(e);
|
printV(e);
|
||||||
throw Exception(PayjoinSessionError.recoverable(e.toString()));
|
throw Exception(PayjoinSessionError.recoverable(e.toString()));
|
||||||
|
@ -65,14 +65,14 @@ class PayjoinSenderWorker {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attempt to send payjoin using the V2 of the protocol.
|
/// Attempt to send payjoin using the V2 of the protocol.
|
||||||
Future<String> _runSenderV2(Sender sender, http.Client httpClient) async {
|
Future<String> _runSenderV2(Sender sender) async {
|
||||||
try {
|
try {
|
||||||
final postRequest = await sender.extractV2(
|
final postRequest = await sender.extractV2(
|
||||||
ohttpProxyUrl:
|
ohttpProxyUrl:
|
||||||
await pj_uri.Url.fromStr(PayjoinManager.randomOhttpRelayUrl()),
|
await pj_uri.Url.fromStr(PayjoinManager.randomOhttpRelayUrl()),
|
||||||
);
|
);
|
||||||
|
|
||||||
final postResult = await _postRequest(httpClient, postRequest.$1);
|
final postResult = await _postRequest(postRequest.$1);
|
||||||
final getContext =
|
final getContext =
|
||||||
await postRequest.$2.processResponse(response: postResult);
|
await postRequest.$2.processResponse(response: postResult);
|
||||||
|
|
||||||
|
@ -84,7 +84,7 @@ class PayjoinSenderWorker {
|
||||||
final getRequest = await getContext.extractReq(
|
final getRequest = await getContext.extractReq(
|
||||||
ohttpRelay: await PayjoinManager.randomOhttpRelayUrl(),
|
ohttpRelay: await PayjoinManager.randomOhttpRelayUrl(),
|
||||||
);
|
);
|
||||||
final getRes = await _postRequest(httpClient, getRequest.$1);
|
final getRes = await _postRequest(getRequest.$1);
|
||||||
final proposalPsbt = await getContext.processResponse(
|
final proposalPsbt = await getContext.processResponse(
|
||||||
response: getRes,
|
response: getRes,
|
||||||
ohttpCtx: getRequest.$2,
|
ohttpCtx: getRequest.$2,
|
||||||
|
@ -98,10 +98,10 @@ class PayjoinSenderWorker {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attempt to send payjoin using the V1 of the protocol.
|
/// Attempt to send payjoin using the V1 of the protocol.
|
||||||
Future<String> _runSenderV1(Sender sender, http.Client httpClient) async {
|
Future<String> _runSenderV1(Sender sender) async {
|
||||||
try {
|
try {
|
||||||
final postRequest = await sender.extractV1();
|
final postRequest = await sender.extractV1();
|
||||||
final response = await _postRequest(httpClient, postRequest.$1);
|
final response = await _postRequest(postRequest.$1);
|
||||||
|
|
||||||
sendPort.send({'type': PayjoinSenderRequestTypes.requestPosted});
|
sendPort.send({'type': PayjoinSenderRequestTypes.requestPosted});
|
||||||
|
|
||||||
|
@ -111,7 +111,7 @@ class PayjoinSenderWorker {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<int>> _postRequest(http.Client client, Request req) async {
|
Future<List<int>> _postRequest(Request req) async {
|
||||||
final httpRequest = await client.post(Uri.parse(req.url.asString()),
|
final httpRequest = await client.post(Uri.parse(req.url.asString()),
|
||||||
headers: {'Content-Type': req.contentType}, body: req.body);
|
headers: {'Content-Type': req.contentType}, body: req.body);
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,14 @@ class PayjoinStorage {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
PayjoinSession? getUnusedActiveReceiverSession(String walletId) =>
|
||||||
|
_payjoinSessionSources.values
|
||||||
|
.where((session) =>
|
||||||
|
session.walletId == walletId &&
|
||||||
|
session.status == PayjoinSessionStatus.created.name &&
|
||||||
|
!session.isSenderSession)
|
||||||
|
.firstOrNull;
|
||||||
|
|
||||||
Future<void> markReceiverSessionComplete(
|
Future<void> markReceiverSessionComplete(
|
||||||
String sessionId, String txId, String amount) async {
|
String sessionId, String txId, String amount) async {
|
||||||
final session = _payjoinSessionSources.get("$_receiverPrefix${sessionId}")!;
|
final session = _payjoinSessionSources.get("$_receiverPrefix${sessionId}")!;
|
||||||
|
@ -76,10 +84,11 @@ class PayjoinStorage {
|
||||||
await session.save();
|
await session.save();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> markSenderSessionUnrecoverable(String pjUrl) async {
|
Future<void> markSenderSessionUnrecoverable(String pjUrl, String reason) async {
|
||||||
final session = _payjoinSessionSources.get("$_senderPrefix$pjUrl")!;
|
final session = _payjoinSessionSources.get("$_senderPrefix$pjUrl")!;
|
||||||
|
|
||||||
session.status = PayjoinSessionStatus.unrecoverable.name;
|
session.status = PayjoinSessionStatus.unrecoverable.name;
|
||||||
|
session.error = reason;
|
||||||
await session.save();
|
await session.save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -948,11 +948,21 @@ packages:
|
||||||
socks5_proxy:
|
socks5_proxy:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: socks5_proxy
|
path: "."
|
||||||
sha256: "616818a0ea1064a4823b53c9f7eaf8da64ed82dcd51ed71371c7e54751ed5053"
|
ref: "27ad7c2efae8d7460325c74b90f660085cbd0685"
|
||||||
url: "https://pub.dev"
|
resolved-ref: "27ad7c2efae8d7460325c74b90f660085cbd0685"
|
||||||
source: hosted
|
url: "https://github.com/LacticWhale/socks_dart"
|
||||||
version: "1.0.6"
|
source: git
|
||||||
|
version: "2.1.0"
|
||||||
|
socks_socket:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
path: "."
|
||||||
|
ref: e6232c53c1595469931ababa878759a067c02e94
|
||||||
|
resolved-ref: e6232c53c1595469931ababa878759a067c02e94
|
||||||
|
url: "https://github.com/sneurlax/socks_socket"
|
||||||
|
source: git
|
||||||
|
version: "1.1.1"
|
||||||
source_gen:
|
source_gen:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -1038,10 +1048,19 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: timing
|
name: timing
|
||||||
sha256: "62ee18aca144e4a9f29d212f5a4c6a053be252b895ab14b5821996cff4ed90fe"
|
sha256: "70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.2"
|
version: "1.0.1"
|
||||||
|
tor_binary:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
path: "."
|
||||||
|
ref: cb811c610871a9517d47134b87c2f590c15c96c5
|
||||||
|
resolved-ref: cb811c610871a9517d47134b87c2f590c15c96c5
|
||||||
|
url: "https://github.com/MrCyjaneK/flutter-tor_binary"
|
||||||
|
source: git
|
||||||
|
version: "4.7.14"
|
||||||
tuple:
|
tuple:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
@ -54,6 +54,10 @@ dependencies:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/cake-tech/ledger-flutter-plus-plugins
|
url: https://github.com/cake-tech/ledger-flutter-plus-plugins
|
||||||
path: packages/ledger-litecoin
|
path: packages/ledger-litecoin
|
||||||
|
socks_socket:
|
||||||
|
git:
|
||||||
|
url: https://github.com/sneurlax/socks_socket
|
||||||
|
ref: e6232c53c1595469931ababa878759a067c02e94
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
|
import 'package:cw_core/utils/proxy_wrapper.dart';
|
||||||
import 'package:cw_core/utils/print_verbose.dart';
|
import 'package:cw_core/utils/print_verbose.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'package:http/http.dart' as http;
|
|
||||||
|
|
||||||
// FIXME: Hardcoded values; Works only for monero
|
// FIXME: Hardcoded values; Works only for monero
|
||||||
|
|
||||||
|
@ -234,10 +234,14 @@ int getHavenHeightByDate({required DateTime date}) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<int> getHavenCurrentHeight() async {
|
Future<int> getHavenCurrentHeight() async {
|
||||||
final response = await http.get(Uri.parse('https://explorer.havenprotocol.org/api/networkinfo'));
|
final req = await ProxyWrapper().getHttpClient()
|
||||||
|
.getUrl(Uri.parse('https://explorer.havenprotocol.org/api/networkinfo'))
|
||||||
|
.timeout(Duration(seconds: 15));
|
||||||
|
final response = await req.close();
|
||||||
|
final stringResponse = await response.transform(utf8.decoder).join();
|
||||||
|
|
||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
final info = jsonDecode(response.body);
|
final info = jsonDecode(stringResponse);
|
||||||
return info['data']['height'] as int;
|
return info['data']['height'] as int;
|
||||||
} else {
|
} else {
|
||||||
throw Exception('Failed to load current blockchain height');
|
throw Exception('Failed to load current blockchain height');
|
||||||
|
@ -269,13 +273,13 @@ const bitcoinDates = {
|
||||||
};
|
};
|
||||||
|
|
||||||
Future<int> getBitcoinHeightByDateAPI({required DateTime date}) async {
|
Future<int> getBitcoinHeightByDateAPI({required DateTime date}) async {
|
||||||
final response = await http.get(
|
final req = await ProxyWrapper().getHttpClient()
|
||||||
Uri.parse(
|
.getUrl(Uri.parse("https://mempool.cakewallet.com/api/v1/mining/blocks/timestamp/${(date.millisecondsSinceEpoch / 1000).round()}"))
|
||||||
"https://mempool.cakewallet.com/api/v1/mining/blocks/timestamp/${(date.millisecondsSinceEpoch / 1000).round()}",
|
.timeout(Duration(seconds: 15));
|
||||||
),
|
final response = await req.close();
|
||||||
);
|
final stringResponse = await response.transform(utf8.decoder).join();
|
||||||
|
|
||||||
return jsonDecode(response.body)['height'] as int;
|
return jsonDecode(stringResponse)['height'] as int;
|
||||||
}
|
}
|
||||||
|
|
||||||
int getBitcoinHeightByDate({required DateTime date}) {
|
int getBitcoinHeightByDate({required DateTime date}) {
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'package:cw_core/keyable.dart';
|
import 'package:cw_core/keyable.dart';
|
||||||
|
import 'package:cw_core/utils/proxy_socket/abstract.dart';
|
||||||
|
import 'package:cw_core/utils/proxy_wrapper.dart';
|
||||||
import 'package:cw_core/utils/print_verbose.dart';
|
import 'package:cw_core/utils/print_verbose.dart';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'package:http/http.dart' as http;
|
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
import 'package:cw_core/hive_type_ids.dart';
|
import 'package:cw_core/hive_type_ids.dart';
|
||||||
import 'package:cw_core/wallet_type.dart';
|
import 'package:cw_core/wallet_type.dart';
|
||||||
import 'package:http/io_client.dart' as ioc;
|
|
||||||
import 'dart:math' as math;
|
import 'dart:math' as math;
|
||||||
import 'package:convert/convert.dart';
|
import 'package:convert/convert.dart';
|
||||||
|
|
||||||
|
@ -184,23 +184,17 @@ class Node extends HiveObject with Keyable {
|
||||||
final body = {'jsonrpc': '2.0', 'id': '0', 'method': "getinfo"};
|
final body = {'jsonrpc': '2.0', 'id': '0', 'method': "getinfo"};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final authenticatingClient = HttpClient();
|
|
||||||
authenticatingClient.badCertificateCallback =
|
|
||||||
((X509Certificate cert, String host, int port) => true);
|
|
||||||
|
|
||||||
final http.Client client = ioc.IOClient(authenticatingClient);
|
|
||||||
|
|
||||||
final jsonBody = json.encode(body);
|
final jsonBody = json.encode(body);
|
||||||
|
|
||||||
final response = await client.post(
|
final response = await ProxyWrapper().post(
|
||||||
rpcUri,
|
clearnetUri: rpcUri,
|
||||||
headers: {'Content-Type': 'application/json'},
|
headers: {'Content-Type': 'application/json'},
|
||||||
body: jsonBody,
|
body: jsonBody,
|
||||||
);
|
);
|
||||||
|
|
||||||
printV("node check response: ${response.body}");
|
|
||||||
|
|
||||||
final resBody = json.decode(response.body) as Map<String, dynamic>;
|
final resBody = json.decode(response.body) as Map<String, dynamic>;
|
||||||
|
|
||||||
return resBody['result']['height'] != null;
|
return resBody['result']['height'] != null;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
printV("error: $e");
|
printV("error: $e");
|
||||||
|
@ -218,11 +212,7 @@ class Node extends HiveObject with Keyable {
|
||||||
final body = {'jsonrpc': '2.0', 'id': '0', 'method': methodName};
|
final body = {'jsonrpc': '2.0', 'id': '0', 'method': methodName};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final authenticatingClient = HttpClient();
|
final client = ProxyWrapper().getHttpIOClient();
|
||||||
authenticatingClient.badCertificateCallback =
|
|
||||||
((X509Certificate cert, String host, int port) => true);
|
|
||||||
|
|
||||||
final http.Client client = ioc.IOClient(authenticatingClient);
|
|
||||||
|
|
||||||
final jsonBody = json.encode(body);
|
final jsonBody = json.encode(body);
|
||||||
|
|
||||||
|
@ -242,15 +232,15 @@ class Node extends HiveObject with Keyable {
|
||||||
return !(response['offline'] as bool);
|
return !(response['offline'] as bool);
|
||||||
}
|
}
|
||||||
|
|
||||||
printV("node check response: ${response.body}");
|
final responseString = await response.body;
|
||||||
|
|
||||||
if ((response.body.contains("400 Bad Request") // Some other generic error
|
if ((responseString.contains("400 Bad Request") // Some other generic error
|
||||||
||
|
||
|
||||||
response.body.contains("plain HTTP request was sent to HTTPS port") // Cloudflare
|
responseString.contains("plain HTTP request was sent to HTTPS port") // Cloudflare
|
||||||
||
|
||
|
||||||
response.headers["location"] != null // Generic reverse proxy
|
response.headers["location"] != null // Generic reverse proxy
|
||||||
||
|
||
|
||||||
response.body
|
responseString
|
||||||
.contains("301 Moved Permanently") // Poorly configured generic reverse proxy
|
.contains("301 Moved Permanently") // Poorly configured generic reverse proxy
|
||||||
) &&
|
) &&
|
||||||
!(useSSL ?? false)) {
|
!(useSSL ?? false)) {
|
||||||
|
@ -277,15 +267,16 @@ class Node extends HiveObject with Keyable {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> requestNodeWithProxy() async {
|
Future<bool> requestNodeWithProxy() async {
|
||||||
if (!isValidProxyAddress /* && !Tor.instance.enabled*/) {
|
if (!isValidProxyAddress && !CakeTor.instance.enabled) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
String? proxy = socksProxyAddress;
|
String? proxy = socksProxyAddress;
|
||||||
|
|
||||||
// if ((proxy?.isEmpty ?? true) && Tor.instance.enabled) {
|
if ((proxy?.isEmpty ?? true) && CakeTor.instance.enabled) {
|
||||||
// proxy = "${InternetAddress.loopbackIPv4.address}:${Tor.instance.port}";
|
proxy = "${InternetAddress.loopbackIPv4.address}:${CakeTor.instance.port}";
|
||||||
// }
|
}
|
||||||
|
printV("proxy: $proxy");
|
||||||
if (proxy == null) {
|
if (proxy == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -305,13 +296,9 @@ class Node extends HiveObject with Keyable {
|
||||||
// you try to communicate with it
|
// you try to communicate with it
|
||||||
Future<bool> requestElectrumServer() async {
|
Future<bool> requestElectrumServer() async {
|
||||||
try {
|
try {
|
||||||
final Socket socket;
|
final ProxySocket socket;
|
||||||
if (useSSL == true) {
|
socket = await ProxyWrapper().getSocksSocket(useSSL ?? false, uri.host, uri.port);
|
||||||
socket = await SecureSocket.connect(uri.host, uri.port,
|
|
||||||
timeout: Duration(seconds: 5), onBadCertificate: (_) => true);
|
|
||||||
} else {
|
|
||||||
socket = await Socket.connect(uri.host, uri.port, timeout: Duration(seconds: 5));
|
|
||||||
}
|
|
||||||
|
|
||||||
socket.destroy();
|
socket.destroy();
|
||||||
return true;
|
return true;
|
||||||
|
@ -322,8 +309,8 @@ class Node extends HiveObject with Keyable {
|
||||||
|
|
||||||
Future<bool> requestNanoNode() async {
|
Future<bool> requestNanoNode() async {
|
||||||
try {
|
try {
|
||||||
final response = await http.post(
|
final response = await ProxyWrapper().post(
|
||||||
uri,
|
clearnetUri: uri,
|
||||||
headers: {"Content-Type": "application/json", "nano-app": "cake-wallet"},
|
headers: {"Content-Type": "application/json", "nano-app": "cake-wallet"},
|
||||||
body: jsonEncode(
|
body: jsonEncode(
|
||||||
{
|
{
|
||||||
|
@ -332,7 +319,8 @@ class Node extends HiveObject with Keyable {
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
final data = await jsonDecode(response.body);
|
|
||||||
|
final data = jsonDecode(response.body);
|
||||||
if (response.statusCode != 200 ||
|
if (response.statusCode != 200 ||
|
||||||
data["error"] != null ||
|
data["error"] != null ||
|
||||||
data["balance"] == null ||
|
data["balance"] == null ||
|
||||||
|
@ -348,13 +336,14 @@ class Node extends HiveObject with Keyable {
|
||||||
|
|
||||||
Future<bool> requestEthereumServer() async {
|
Future<bool> requestEthereumServer() async {
|
||||||
try {
|
try {
|
||||||
final response = await http.get(
|
final req = await ProxyWrapper().getHttpClient()
|
||||||
uri,
|
.getUrl(uri,)
|
||||||
headers: {'Content-Type': 'application/json'},
|
.timeout(Duration(seconds: 15));
|
||||||
);
|
final response = await req.close();
|
||||||
|
|
||||||
return response.statusCode >= 200 && response.statusCode < 300;
|
return response.statusCode >= 200 && response.statusCode < 300;
|
||||||
} catch (_) {
|
} catch (err) {
|
||||||
|
printV("Failed to request ethereum server: $err");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -462,7 +451,7 @@ class DaemonRpc {
|
||||||
|
|
||||||
/// Perform a JSON-RPC call with Digest Authentication.
|
/// Perform a JSON-RPC call with Digest Authentication.
|
||||||
Future<Map<String, dynamic>> call(String method, Map<String, dynamic> params) async {
|
Future<Map<String, dynamic>> call(String method, Map<String, dynamic> params) async {
|
||||||
final http.Client client = http.Client();
|
final client = ProxyWrapper().getHttpIOClient();
|
||||||
final DigestAuth digestAuth = DigestAuth(username, password);
|
final DigestAuth digestAuth = DigestAuth(username, password);
|
||||||
|
|
||||||
// Initial request to get the `WWW-Authenticate` header.
|
// Initial request to get the `WWW-Authenticate` header.
|
||||||
|
|
|
@ -1,20 +1,19 @@
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'package:http/http.dart';
|
import 'package:cw_core/utils/proxy_wrapper.dart';
|
||||||
import 'package:on_chain/solana/solana.dart';
|
import 'package:on_chain/solana/solana.dart';
|
||||||
|
|
||||||
class SolanaRPCHTTPService implements SolanaJSONRPCService {
|
class SolanaRPCHTTPService implements SolanaJSONRPCService {
|
||||||
SolanaRPCHTTPService(
|
SolanaRPCHTTPService(
|
||||||
{required this.url, Client? client, this.defaultRequestTimeout = const Duration(seconds: 30)})
|
{required this.url,
|
||||||
: client = client ?? Client();
|
this.defaultRequestTimeout = const Duration(seconds: 30)});
|
||||||
@override
|
@override
|
||||||
final String url;
|
final String url;
|
||||||
final Client client;
|
|
||||||
final Duration defaultRequestTimeout;
|
final Duration defaultRequestTimeout;
|
||||||
|
|
||||||
@override
|
Future<Map<String, dynamic>> call(SolanaRequestDetails params,
|
||||||
Future<Map<String, dynamic>> call(SolanaRequestDetails params, [Duration? timeout]) async {
|
[Duration? timeout]) async {
|
||||||
final response = await client.post(
|
final response = await ProxyWrapper().post(
|
||||||
Uri.parse(url),
|
clearnetUri: Uri.parse(url),
|
||||||
body: params.toRequestBody(),
|
body: params.toRequestBody(),
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
|
|
29
cw_core/lib/utils/proxy_logger/abstract.dart
Normal file
29
cw_core/lib/utils/proxy_logger/abstract.dart
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
import 'dart:typed_data';
|
||||||
|
import 'package:http/http.dart' as very_insecure_http_do_not_use;
|
||||||
|
|
||||||
|
enum RequestNetwork {
|
||||||
|
clearnet,
|
||||||
|
tor,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum RequestMethod {
|
||||||
|
get,
|
||||||
|
post,
|
||||||
|
put,
|
||||||
|
delete,
|
||||||
|
|
||||||
|
newHttpClient,
|
||||||
|
newHttpIOClient,
|
||||||
|
newProxySocket,
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class ProxyLogger {
|
||||||
|
void log({
|
||||||
|
required Uri? uri,
|
||||||
|
required RequestMethod method,
|
||||||
|
required Uint8List body,
|
||||||
|
required very_insecure_http_do_not_use.Response? response,
|
||||||
|
required RequestNetwork network,
|
||||||
|
required String? error,
|
||||||
|
});
|
||||||
|
}
|
63
cw_core/lib/utils/proxy_logger/memory_proxy_logger.dart
Normal file
63
cw_core/lib/utils/proxy_logger/memory_proxy_logger.dart
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:cw_core/utils/proxy_logger/abstract.dart';
|
||||||
|
import 'package:http/http.dart' as very_insecure_http_do_not_use;
|
||||||
|
|
||||||
|
class MemoryProxyLoggerEntry {
|
||||||
|
MemoryProxyLoggerEntry({
|
||||||
|
required this.trace,
|
||||||
|
required this.uri,
|
||||||
|
required this.body,
|
||||||
|
required this.network,
|
||||||
|
required this.method,
|
||||||
|
required this.response,
|
||||||
|
required this.error,
|
||||||
|
}) : time = DateTime.now();
|
||||||
|
|
||||||
|
final StackTrace trace;
|
||||||
|
final Uri? uri;
|
||||||
|
final Uint8List body;
|
||||||
|
final RequestNetwork network;
|
||||||
|
final very_insecure_http_do_not_use.Response? response;
|
||||||
|
final RequestMethod method;
|
||||||
|
final String? error;
|
||||||
|
final DateTime time;
|
||||||
|
@override
|
||||||
|
String toString() => """MemoryProxyLoggerEntry(
|
||||||
|
uri: $uri,
|
||||||
|
body: $body,
|
||||||
|
network: $network,
|
||||||
|
method: $method,
|
||||||
|
response:
|
||||||
|
code: ${response?.statusCode},
|
||||||
|
headers: ${response?.headers},
|
||||||
|
body: ${response?.body},
|
||||||
|
error: $error,
|
||||||
|
time: $time,
|
||||||
|
trace: ${trace}
|
||||||
|
);""";
|
||||||
|
}
|
||||||
|
|
||||||
|
class MemoryProxyLogger implements ProxyLogger {
|
||||||
|
static List<MemoryProxyLoggerEntry> logs = [];
|
||||||
|
@override
|
||||||
|
void log({
|
||||||
|
required Uri? uri,
|
||||||
|
required RequestMethod method,
|
||||||
|
required Uint8List body,
|
||||||
|
required very_insecure_http_do_not_use.Response? response,
|
||||||
|
required RequestNetwork network,
|
||||||
|
required String? error,
|
||||||
|
}) {
|
||||||
|
final trace = StackTrace.current;
|
||||||
|
logs.add(MemoryProxyLoggerEntry(
|
||||||
|
method: method,
|
||||||
|
trace: trace,
|
||||||
|
uri: uri,
|
||||||
|
body: body,
|
||||||
|
network: network,
|
||||||
|
response: response,
|
||||||
|
error: error,),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
17
cw_core/lib/utils/proxy_logger/silent_logger.dart
Normal file
17
cw_core/lib/utils/proxy_logger/silent_logger.dart
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:cw_core/utils/proxy_logger/abstract.dart';
|
||||||
|
import 'package:http/http.dart' as very_insecure_http_do_not_use;
|
||||||
|
|
||||||
|
// we are not doing anything
|
||||||
|
class SilentProxyLogger implements ProxyLogger {
|
||||||
|
@override
|
||||||
|
void log({
|
||||||
|
required Uri? uri,
|
||||||
|
required RequestMethod method,
|
||||||
|
required Uint8List body,
|
||||||
|
required very_insecure_http_do_not_use.Response? response,
|
||||||
|
required RequestNetwork network,
|
||||||
|
required String? error,
|
||||||
|
}) {}
|
||||||
|
}
|
47
cw_core/lib/utils/proxy_socket/abstract.dart
Normal file
47
cw_core/lib/utils/proxy_socket/abstract.dart
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
import 'dart:async';
|
||||||
|
import 'dart:io';
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:cw_core/utils/proxy_socket/insecure.dart';
|
||||||
|
import 'package:cw_core/utils/proxy_socket/secure.dart';
|
||||||
|
import 'package:cw_core/utils/proxy_socket/socks.dart';
|
||||||
|
import 'package:cw_core/utils/proxy_wrapper.dart';
|
||||||
|
import 'package:socks_socket/socks_socket.dart';
|
||||||
|
|
||||||
|
class ProxyAddress {
|
||||||
|
final String host;
|
||||||
|
final int port;
|
||||||
|
|
||||||
|
ProxyAddress({required this.host, required this.port});
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class ProxySocket {
|
||||||
|
static Future<ProxySocket> connect(bool sslEnabled, ProxyAddress address, {Duration? connectionTimeout}) async {
|
||||||
|
if (CakeTor.instance.started) {
|
||||||
|
var socksSocket = await SOCKSSocket.create(
|
||||||
|
proxyHost: InternetAddress.loopbackIPv4.address,
|
||||||
|
proxyPort: CakeTor.instance.port,
|
||||||
|
sslEnabled: sslEnabled,
|
||||||
|
);
|
||||||
|
await socksSocket.connect();
|
||||||
|
await socksSocket.connectTo(address.host, address.port);
|
||||||
|
return ProxySocketSocks(socksSocket);
|
||||||
|
}
|
||||||
|
if (sslEnabled == false) {
|
||||||
|
return ProxySocketInsecure(await Socket.connect(address.host, address.port, timeout: connectionTimeout));
|
||||||
|
} else {
|
||||||
|
return ProxySocketSecure(await SecureSocket.connect(
|
||||||
|
address.host,
|
||||||
|
address.port,
|
||||||
|
timeout: connectionTimeout,
|
||||||
|
onBadCertificate: (_) => true,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> close();
|
||||||
|
Future<void> destroy();
|
||||||
|
Future<void> write(String data);
|
||||||
|
StreamSubscription<List<int>> listen(Function(Uint8List event) onData, {Function (Object error)? onError, Function ()? onDone, bool cancelOnError = true});
|
||||||
|
ProxyAddress get address;
|
||||||
|
}
|
34
cw_core/lib/utils/proxy_socket/insecure.dart
Normal file
34
cw_core/lib/utils/proxy_socket/insecure.dart
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
|
||||||
|
import 'package:cw_core/utils/proxy_socket/abstract.dart';
|
||||||
|
import 'dart:async';
|
||||||
|
import 'dart:typed_data';
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
class ProxySocketInsecure implements ProxySocket {
|
||||||
|
final Socket socket;
|
||||||
|
|
||||||
|
ProxySocketInsecure(this.socket);
|
||||||
|
|
||||||
|
ProxyAddress get address => ProxyAddress(host: socket.remoteAddress.host, port: socket.remotePort);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> close() => socket.close();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> destroy() async => socket.destroy();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> write(String data) async => socket.write(data);
|
||||||
|
|
||||||
|
@override
|
||||||
|
StreamSubscription<List<int>> listen(Function(Uint8List event) onData, {Function(Object error)? onError, Function()? onDone, bool cancelOnError = true}) {
|
||||||
|
return socket.listen(
|
||||||
|
(data) {
|
||||||
|
onData(Uint8List.fromList(data));
|
||||||
|
},
|
||||||
|
onError: onError,
|
||||||
|
onDone: onDone,
|
||||||
|
cancelOnError: cancelOnError,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
34
cw_core/lib/utils/proxy_socket/secure.dart
Normal file
34
cw_core/lib/utils/proxy_socket/secure.dart
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
import 'dart:async';
|
||||||
|
import 'dart:io';
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:cw_core/utils/proxy_socket/abstract.dart';
|
||||||
|
|
||||||
|
class ProxySocketSecure implements ProxySocket {
|
||||||
|
final SecureSocket socket;
|
||||||
|
|
||||||
|
ProxySocketSecure(this.socket);
|
||||||
|
|
||||||
|
ProxyAddress get address => ProxyAddress(host: socket.remoteAddress.host, port: socket.remotePort);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> close() => socket.close();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> destroy() async => socket.destroy();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> write(String data) async => socket.write(data);
|
||||||
|
|
||||||
|
@override
|
||||||
|
StreamSubscription<List<int>> listen(Function(Uint8List event) onData, {Function(Object error)? onError, Function()? onDone, bool cancelOnError = true}) {
|
||||||
|
return socket.listen(
|
||||||
|
(data) {
|
||||||
|
onData(Uint8List.fromList(data));
|
||||||
|
},
|
||||||
|
onError: onError,
|
||||||
|
onDone: onDone,
|
||||||
|
cancelOnError: cancelOnError,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
36
cw_core/lib/utils/proxy_socket/socks.dart
Normal file
36
cw_core/lib/utils/proxy_socket/socks.dart
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
import 'dart:async';
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:cw_core/utils/proxy_socket/abstract.dart';
|
||||||
|
import 'package:socks_socket/socks_socket.dart';
|
||||||
|
|
||||||
|
class ProxySocketSocks implements ProxySocket {
|
||||||
|
final SOCKSSocket socket;
|
||||||
|
|
||||||
|
ProxySocketSocks(this.socket);
|
||||||
|
|
||||||
|
@override
|
||||||
|
ProxyAddress get address => ProxyAddress(host: socket.proxyHost, port: socket.proxyPort);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> close() => socket.close();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> destroy() => close();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> write(String data) async => socket.write(data);
|
||||||
|
|
||||||
|
@override
|
||||||
|
StreamSubscription<List<int>> listen(Function(Uint8List event) onData, {Function(Object error)? onError, Function()? onDone, bool cancelOnError = true}) {
|
||||||
|
return socket.listen(
|
||||||
|
(data) {
|
||||||
|
onData(Uint8List.fromList(data));
|
||||||
|
},
|
||||||
|
onError: onError,
|
||||||
|
onDone: onDone,
|
||||||
|
cancelOnError: cancelOnError,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
447
cw_core/lib/utils/proxy_wrapper.dart
Normal file
447
cw_core/lib/utils/proxy_wrapper.dart
Normal file
|
@ -0,0 +1,447 @@
|
||||||
|
import 'dart:async';
|
||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:io';
|
||||||
|
import 'dart:typed_data';
|
||||||
|
import 'package:cw_core/utils/proxy_logger/abstract.dart';
|
||||||
|
import 'package:cw_core/utils/proxy_socket/abstract.dart';
|
||||||
|
import 'package:cw_core/utils/tor/abstract.dart';
|
||||||
|
import 'package:cw_core/utils/tor/android.dart';
|
||||||
|
import 'package:cw_core/utils/tor/disabled.dart';
|
||||||
|
import 'package:http/http.dart';
|
||||||
|
import 'package:socks5_proxy/socks_client.dart';
|
||||||
|
import 'package:http/io_client.dart' as ioc;
|
||||||
|
|
||||||
|
class ProxyWrapper {
|
||||||
|
static final ProxyWrapper _proxyWrapper = ProxyWrapper._internal();
|
||||||
|
static ProxyLogger? logger;
|
||||||
|
|
||||||
|
factory ProxyWrapper() {
|
||||||
|
return _proxyWrapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
ProxyWrapper._internal();
|
||||||
|
Future<ProxySocket> getSocksSocket(bool sslEnabled, String host, int port, {Duration? connectionTimeout}) async {
|
||||||
|
logger?.log(
|
||||||
|
uri: Uri(
|
||||||
|
scheme: sslEnabled ? "https" : "http",
|
||||||
|
host: host,
|
||||||
|
port: port,
|
||||||
|
),
|
||||||
|
method: RequestMethod.newProxySocket,
|
||||||
|
body: Uint8List(0),
|
||||||
|
response: null,
|
||||||
|
network: requestNetwork(),
|
||||||
|
error: null
|
||||||
|
);
|
||||||
|
return ProxySocket.connect(sslEnabled, ProxyAddress(host: host, port: port), connectionTimeout: connectionTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
RequestNetwork requestNetwork() {
|
||||||
|
return CakeTor.instance.started ? RequestNetwork.tor : RequestNetwork.clearnet;
|
||||||
|
}
|
||||||
|
|
||||||
|
ioc.IOClient getHttpIOClient({int? portOverride, bool internal = false}) {
|
||||||
|
if (!internal) {
|
||||||
|
logger?.log(
|
||||||
|
uri: null,
|
||||||
|
method: RequestMethod.newHttpIOClient,
|
||||||
|
body: Uint8List(0),
|
||||||
|
response: null,
|
||||||
|
network: requestNetwork(),
|
||||||
|
error: null,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// ignore: deprecated_member_use_from_same_package
|
||||||
|
final httpClient = ProxyWrapper().getHttpClient(portOverride: portOverride, internal: true);
|
||||||
|
return ioc.IOClient(httpClient);
|
||||||
|
}
|
||||||
|
|
||||||
|
int getPort() => CakeTor.instance.port;
|
||||||
|
|
||||||
|
@Deprecated('Use ProxyWrapper().get/post/put methods instead, and provide proper clearnet and onion uri.')
|
||||||
|
HttpClient getHttpClient({int? portOverride, bool internal = false}) {
|
||||||
|
if (!internal) {
|
||||||
|
logger?.log(
|
||||||
|
uri: null,
|
||||||
|
method: RequestMethod.newProxySocket,
|
||||||
|
body: Uint8List(0),
|
||||||
|
response: null,
|
||||||
|
network: requestNetwork(),
|
||||||
|
error: null
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (CakeTor.instance.started) {
|
||||||
|
// Assign connection factory.
|
||||||
|
final client = HttpClient();
|
||||||
|
SocksTCPClient.assignToHttpClient(client, [
|
||||||
|
ProxySettings(
|
||||||
|
InternetAddress.loopbackIPv4,
|
||||||
|
CakeTor.instance.port,
|
||||||
|
password: null,
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
return client;
|
||||||
|
} else {
|
||||||
|
return HttpClient();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Future<Response> _make({
|
||||||
|
required RequestMethod method,
|
||||||
|
required ioc.IOClient client,
|
||||||
|
required Uri uri,
|
||||||
|
required Map<String, String>? headers,
|
||||||
|
String? body,
|
||||||
|
}) async {
|
||||||
|
Object? error;
|
||||||
|
Response? resp;
|
||||||
|
try {
|
||||||
|
switch (method) {
|
||||||
|
case RequestMethod.get:
|
||||||
|
resp = await client. get(
|
||||||
|
uri,
|
||||||
|
headers: headers,
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case RequestMethod.delete:
|
||||||
|
resp = await client.delete(
|
||||||
|
uri,
|
||||||
|
headers: headers,
|
||||||
|
body: body,
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case RequestMethod.post:
|
||||||
|
resp = await client.post(
|
||||||
|
uri,
|
||||||
|
headers: headers,
|
||||||
|
body: body,
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case RequestMethod.put:
|
||||||
|
resp = await client.put(
|
||||||
|
uri,
|
||||||
|
headers: headers,
|
||||||
|
body: body,
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case RequestMethod.newHttpClient:
|
||||||
|
case RequestMethod.newHttpIOClient:
|
||||||
|
case RequestMethod.newProxySocket:
|
||||||
|
throw UnimplementedError();
|
||||||
|
}
|
||||||
|
return resp;
|
||||||
|
} catch (e) {
|
||||||
|
error = e;
|
||||||
|
rethrow;
|
||||||
|
} finally {
|
||||||
|
logger?.log(
|
||||||
|
uri: uri,
|
||||||
|
method: RequestMethod.get,
|
||||||
|
body: utf8.encode(body ?? ''),
|
||||||
|
response: resp,
|
||||||
|
network: requestNetwork(),
|
||||||
|
error: error?.toString(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Response> get({
|
||||||
|
Map<String, String>? headers,
|
||||||
|
int? portOverride,
|
||||||
|
Uri? clearnetUri,
|
||||||
|
Uri? onionUri,
|
||||||
|
}) async {
|
||||||
|
ioc.IOClient? torClient;
|
||||||
|
bool torEnabled = CakeTor.instance.started;
|
||||||
|
|
||||||
|
if (CakeTor.instance.started) {
|
||||||
|
torEnabled = true;
|
||||||
|
} else {
|
||||||
|
torEnabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if tor is enabled, try to connect to the onion url first:
|
||||||
|
if (torEnabled) {
|
||||||
|
try {
|
||||||
|
// ignore: deprecated_member_use_from_same_package
|
||||||
|
torClient = await getHttpIOClient(portOverride: portOverride, internal: true);
|
||||||
|
} catch (_) {
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (onionUri != null) {
|
||||||
|
try {
|
||||||
|
return await _make(
|
||||||
|
method: RequestMethod.get,
|
||||||
|
client: torClient,
|
||||||
|
uri: onionUri,
|
||||||
|
headers: headers,
|
||||||
|
);
|
||||||
|
} catch (_) {
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clearnetUri != null) {
|
||||||
|
try {
|
||||||
|
return await _make(
|
||||||
|
method: RequestMethod.get,
|
||||||
|
client: torClient,
|
||||||
|
uri: clearnetUri,
|
||||||
|
headers: headers,
|
||||||
|
);
|
||||||
|
} catch (_) {
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clearnetUri != null) {
|
||||||
|
try {
|
||||||
|
return HttpOverrides.runZoned(
|
||||||
|
() async {
|
||||||
|
return await _make(
|
||||||
|
method: RequestMethod.get,
|
||||||
|
client: ioc.IOClient(),
|
||||||
|
uri: clearnetUri,
|
||||||
|
headers: headers,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} catch (_) {
|
||||||
|
// we weren't able to get a response:
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw Exception("Unable to connect to server");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Future<Response> post({
|
||||||
|
Map<String, String>? headers,
|
||||||
|
int? portOverride,
|
||||||
|
Uri? clearnetUri,
|
||||||
|
Uri? onionUri,
|
||||||
|
String? body,
|
||||||
|
bool allowMitmMoneroBypassSSLCheck = false,
|
||||||
|
}) async {
|
||||||
|
HttpClient? torHttpClient;
|
||||||
|
HttpClient cleatnetHttpClient = HttpClient();
|
||||||
|
if (allowMitmMoneroBypassSSLCheck) {
|
||||||
|
cleatnetHttpClient.badCertificateCallback =
|
||||||
|
((X509Certificate cert, String host, int port) => true);
|
||||||
|
}
|
||||||
|
|
||||||
|
ioc.IOClient clearnetClient = ioc.IOClient(cleatnetHttpClient);
|
||||||
|
|
||||||
|
|
||||||
|
bool torEnabled = CakeTor.instance.started;
|
||||||
|
|
||||||
|
if (torEnabled) {
|
||||||
|
try {
|
||||||
|
// ignore: deprecated_member_use_from_same_package
|
||||||
|
torHttpClient = await getHttpClient(portOverride: portOverride);
|
||||||
|
} catch (_) {
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
if (allowMitmMoneroBypassSSLCheck) {
|
||||||
|
torHttpClient.badCertificateCallback =
|
||||||
|
((X509Certificate cert, String host, int port) => true);
|
||||||
|
}
|
||||||
|
if (onionUri != null) {
|
||||||
|
try {
|
||||||
|
return await _make(
|
||||||
|
method: RequestMethod.post,
|
||||||
|
client: ioc.IOClient(torHttpClient),
|
||||||
|
uri: onionUri,
|
||||||
|
headers: headers,
|
||||||
|
body: body,
|
||||||
|
);
|
||||||
|
} catch (_) {
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clearnetUri != null) {
|
||||||
|
try {
|
||||||
|
return await _make(
|
||||||
|
method: RequestMethod.post,
|
||||||
|
client: ioc.IOClient(torHttpClient),
|
||||||
|
uri: clearnetUri,
|
||||||
|
headers: headers,
|
||||||
|
body: body,
|
||||||
|
);
|
||||||
|
} catch (_) {
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clearnetUri != null) {
|
||||||
|
try {
|
||||||
|
return HttpOverrides.runZoned(
|
||||||
|
() async {
|
||||||
|
return await _make(
|
||||||
|
method: RequestMethod.post,
|
||||||
|
client: clearnetClient,
|
||||||
|
uri: clearnetUri,
|
||||||
|
headers: headers,
|
||||||
|
body: body,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} catch (_) {
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw Exception("Unable to connect to server");
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Response> put({
|
||||||
|
Map<String, String>? headers,
|
||||||
|
int? portOverride,
|
||||||
|
Uri? clearnetUri,
|
||||||
|
Uri? onionUri,
|
||||||
|
String? body,
|
||||||
|
}) async {
|
||||||
|
ioc.IOClient? torClient;
|
||||||
|
bool torEnabled = CakeTor.instance.started;
|
||||||
|
|
||||||
|
if (torEnabled) {
|
||||||
|
try {
|
||||||
|
// ignore: deprecated_member_use_from_same_package
|
||||||
|
torClient = await getHttpIOClient(portOverride: portOverride, internal: true);
|
||||||
|
} catch (_) {}
|
||||||
|
|
||||||
|
if (onionUri != null) {
|
||||||
|
try {
|
||||||
|
return await _make(
|
||||||
|
method: RequestMethod.put,
|
||||||
|
client: torClient!,
|
||||||
|
uri: onionUri,
|
||||||
|
headers: headers,
|
||||||
|
body: body,
|
||||||
|
);
|
||||||
|
} catch (_) {
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clearnetUri != null) {
|
||||||
|
try {
|
||||||
|
return await _make(
|
||||||
|
method: RequestMethod.put,
|
||||||
|
client: torClient!,
|
||||||
|
uri: clearnetUri,
|
||||||
|
headers: headers,
|
||||||
|
body: body,
|
||||||
|
);
|
||||||
|
} catch (_) {
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clearnetUri != null) {
|
||||||
|
try {
|
||||||
|
return HttpOverrides.runZoned(
|
||||||
|
() async {
|
||||||
|
return await _make(
|
||||||
|
method: RequestMethod.put,
|
||||||
|
client: ioc.IOClient(),
|
||||||
|
uri: clearnetUri,
|
||||||
|
headers: headers,
|
||||||
|
body: body,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} catch (_) {
|
||||||
|
// we weren't able to get a response:
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw Exception("Unable to connect to server");
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Response> delete({
|
||||||
|
Map<String, String>? headers,
|
||||||
|
int? portOverride,
|
||||||
|
Uri? clearnetUri,
|
||||||
|
Uri? onionUri,
|
||||||
|
}) async {
|
||||||
|
ioc.IOClient? torClient;
|
||||||
|
bool torEnabled = CakeTor.instance.started;
|
||||||
|
|
||||||
|
if (CakeTor.instance.started) {
|
||||||
|
torEnabled = true;
|
||||||
|
} else {
|
||||||
|
torEnabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if tor is enabled, try to connect to the onion url first:
|
||||||
|
if (torEnabled) {
|
||||||
|
try {
|
||||||
|
// ignore: deprecated_member_use_from_same_package
|
||||||
|
torClient = await getHttpIOClient(portOverride: portOverride, internal: true);
|
||||||
|
} catch (_) {
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (onionUri != null) {
|
||||||
|
try {
|
||||||
|
return await _make(
|
||||||
|
method: RequestMethod.delete,
|
||||||
|
client: torClient,
|
||||||
|
uri: onionUri,
|
||||||
|
headers: headers,
|
||||||
|
);
|
||||||
|
} catch (_) {
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clearnetUri != null) {
|
||||||
|
try {
|
||||||
|
return await _make(
|
||||||
|
method: RequestMethod.delete,
|
||||||
|
client: torClient,
|
||||||
|
uri: clearnetUri,
|
||||||
|
headers: headers,
|
||||||
|
);
|
||||||
|
} catch (_) {
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clearnetUri != null) {
|
||||||
|
try {
|
||||||
|
return HttpOverrides.runZoned(
|
||||||
|
() async {
|
||||||
|
return await _make(
|
||||||
|
method: RequestMethod.delete,
|
||||||
|
client: ioc.IOClient(),
|
||||||
|
uri: clearnetUri,
|
||||||
|
headers: headers,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} catch (_) {
|
||||||
|
// we weren't able to get a response:
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw Exception("Unable to connect to server");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class CakeTor {
|
||||||
|
static final CakeTorInstance instance = CakeTorInstance.getInstance();
|
||||||
|
}
|
38
cw_core/lib/utils/tor/abstract.dart
Normal file
38
cw_core/lib/utils/tor/abstract.dart
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:cw_core/utils/print_verbose.dart';
|
||||||
|
import 'package:cw_core/utils/tor/android.dart';
|
||||||
|
import 'package:cw_core/utils/tor/disabled.dart';
|
||||||
|
import 'package:cw_core/utils/tor/tails.dart';
|
||||||
|
|
||||||
|
abstract class CakeTorInstance {
|
||||||
|
bool get started;
|
||||||
|
|
||||||
|
int get port => -1;
|
||||||
|
|
||||||
|
bool get enabled => false;
|
||||||
|
|
||||||
|
bool get bootstrapped => false;
|
||||||
|
|
||||||
|
Future<void> start();
|
||||||
|
Future<void> stop();
|
||||||
|
|
||||||
|
static CakeTorInstance getInstance() {
|
||||||
|
if (Platform.isAndroid) {
|
||||||
|
return CakeTorAndroid();
|
||||||
|
}
|
||||||
|
if (Platform.isLinux) {
|
||||||
|
try {
|
||||||
|
final os = File("/etc/os-release").readAsLinesSync();
|
||||||
|
for (var line in os) {
|
||||||
|
if (!line.startsWith("ID=")) continue;
|
||||||
|
if (!line.contains("tails")) continue;
|
||||||
|
return CakeTorTails();
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
printV("Failed to identify linux version - /etc/os-release missing");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return CakeTorDisabled();
|
||||||
|
}
|
||||||
|
}
|
73
cw_core/lib/utils/tor/android.dart
Normal file
73
cw_core/lib/utils/tor/android.dart
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:cw_core/utils/print_verbose.dart';
|
||||||
|
import 'package:cw_core/utils/tor/abstract.dart';
|
||||||
|
import 'package:path_provider/path_provider.dart';
|
||||||
|
import 'package:path/path.dart' as p;
|
||||||
|
import 'package:tor_binary/tor_binary_platform_interface.dart';
|
||||||
|
|
||||||
|
class CakeTorAndroid implements CakeTorInstance {
|
||||||
|
@override
|
||||||
|
bool get bootstrapped => _proc != null;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool get enabled => _proc != null;
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get port => 42142;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> start() async {
|
||||||
|
await _runEmbeddedTor();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool get started => _proc != null;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> stop() async {
|
||||||
|
_proc?.kill();
|
||||||
|
await _proc?.exitCode;
|
||||||
|
_proc = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Process? _proc;
|
||||||
|
|
||||||
|
Future<void> _runEmbeddedTor() async {
|
||||||
|
final dir = await getApplicationCacheDirectory();
|
||||||
|
|
||||||
|
final torBinPath = p.join((await TorBinaryPlatform.instance.getBinaryPath())!, "libtor.so");
|
||||||
|
printV("torPath: $torBinPath");
|
||||||
|
|
||||||
|
if (started) {
|
||||||
|
printV("Proxy is running");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
printV("Starting embedded tor");
|
||||||
|
printV("app docs: $dir");
|
||||||
|
final torrc = """
|
||||||
|
SocksPort $port
|
||||||
|
Log notice file ${p.join(dir.path, "tor.log")}
|
||||||
|
RunAsDaemon 0
|
||||||
|
DataDirectory ${p.join(dir.path, "tor-data")}
|
||||||
|
""";
|
||||||
|
final torrcPath = p.join(dir.absolute.path, "torrc");
|
||||||
|
File(torrcPath).writeAsStringSync(torrc);
|
||||||
|
|
||||||
|
if (_proc != null) {
|
||||||
|
try {
|
||||||
|
_proc?.kill();
|
||||||
|
await _proc?.exitCode;
|
||||||
|
_proc = null;
|
||||||
|
} catch (e) {
|
||||||
|
printV(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printV("path: $torBinPath -f $torrcPath");
|
||||||
|
_proc = await Process.start(torBinPath, ["-f", torrcPath]);
|
||||||
|
_proc?.stdout.transform(utf8.decoder).forEach(printV);
|
||||||
|
_proc?.stderr.transform(utf8.decoder).forEach(printV);
|
||||||
|
}
|
||||||
|
}
|
21
cw_core/lib/utils/tor/disabled.dart
Normal file
21
cw_core/lib/utils/tor/disabled.dart
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
import 'package:cw_core/utils/tor/abstract.dart';
|
||||||
|
|
||||||
|
class CakeTorDisabled implements CakeTorInstance {
|
||||||
|
@override
|
||||||
|
bool get bootstrapped => false;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool get enabled => false;
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get port => -1;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> start() => throw UnimplementedError();
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool get started => false;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> stop() => throw UnimplementedError();
|
||||||
|
}
|
21
cw_core/lib/utils/tor/tails.dart
Normal file
21
cw_core/lib/utils/tor/tails.dart
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
import 'package:cw_core/utils/tor/abstract.dart';
|
||||||
|
|
||||||
|
class CakeTorTails implements CakeTorInstance {
|
||||||
|
@override
|
||||||
|
bool get bootstrapped => true;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool get enabled => true;
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get port => 9150;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> start() async {}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool get started => true;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> stop() async {}
|
||||||
|
}
|
|
@ -479,7 +479,7 @@ packages:
|
||||||
description:
|
description:
|
||||||
path: "."
|
path: "."
|
||||||
ref: cake-update-v2
|
ref: cake-update-v2
|
||||||
resolved-ref: "01cbbacbb05d2113aafa8b7c4a2bb766f749d8d8"
|
resolved-ref: "096865a8c6b89c260beadfec04f7e184c40a3273"
|
||||||
url: "https://github.com/cake-tech/on_chain.git"
|
url: "https://github.com/cake-tech/on_chain.git"
|
||||||
source: git
|
source: git
|
||||||
version: "3.7.0"
|
version: "3.7.0"
|
||||||
|
@ -635,11 +635,21 @@ packages:
|
||||||
socks5_proxy:
|
socks5_proxy:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: socks5_proxy
|
path: "."
|
||||||
sha256: "616818a0ea1064a4823b53c9f7eaf8da64ed82dcd51ed71371c7e54751ed5053"
|
ref: "27ad7c2efae8d7460325c74b90f660085cbd0685"
|
||||||
url: "https://pub.dev"
|
resolved-ref: "27ad7c2efae8d7460325c74b90f660085cbd0685"
|
||||||
source: hosted
|
url: "https://github.com/LacticWhale/socks_dart"
|
||||||
version: "1.0.6"
|
source: git
|
||||||
|
version: "2.1.0"
|
||||||
|
socks_socket:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
path: "."
|
||||||
|
ref: e6232c53c1595469931ababa878759a067c02e94
|
||||||
|
resolved-ref: e6232c53c1595469931ababa878759a067c02e94
|
||||||
|
url: "https://github.com/sneurlax/socks_socket"
|
||||||
|
source: git
|
||||||
|
version: "1.1.1"
|
||||||
source_gen:
|
source_gen:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -716,10 +726,19 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: timing
|
name: timing
|
||||||
sha256: "62ee18aca144e4a9f29d212f5a4c6a053be252b895ab14b5821996cff4ed90fe"
|
sha256: "70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.2"
|
version: "1.0.1"
|
||||||
|
tor_binary:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
path: "."
|
||||||
|
ref: cb811c610871a9517d47134b87c2f590c15c96c5
|
||||||
|
resolved-ref: cb811c610871a9517d47134b87c2f590c15c96c5
|
||||||
|
url: "https://github.com/MrCyjaneK/flutter-tor_binary"
|
||||||
|
source: git
|
||||||
|
version: "4.7.14"
|
||||||
tuple:
|
tuple:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
@ -25,16 +25,23 @@ dependencies:
|
||||||
url: https://github.com/cake-tech/cake_backup.git
|
url: https://github.com/cake-tech/cake_backup.git
|
||||||
ref: main
|
ref: main
|
||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
socks5_proxy: ^1.0.4
|
socks5_proxy:
|
||||||
|
git:
|
||||||
|
url: https://github.com/LacticWhale/socks_dart
|
||||||
|
ref: 27ad7c2efae8d7460325c74b90f660085cbd0685
|
||||||
unorm_dart: ^0.3.0
|
unorm_dart: ^0.3.0
|
||||||
on_chain:
|
on_chain:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/cake-tech/on_chain.git
|
url: https://github.com/cake-tech/on_chain.git
|
||||||
ref: cake-update-v2
|
ref: cake-update-v2
|
||||||
# tor:
|
socks_socket:
|
||||||
# git:
|
git:
|
||||||
# url: https://github.com/cake-tech/tor.git
|
url: https://github.com/sneurlax/socks_socket
|
||||||
# ref: main
|
ref: e6232c53c1595469931ababa878759a067c02e94
|
||||||
|
tor_binary:
|
||||||
|
git:
|
||||||
|
url: https://github.com/MrCyjaneK/flutter-tor_binary
|
||||||
|
ref: cb811c610871a9517d47134b87c2f590c15c96c5
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|
|
@ -5,6 +5,8 @@ import 'package:path/path.dart' as p;
|
||||||
import 'package:cw_core/exceptions.dart';
|
import 'package:cw_core/exceptions.dart';
|
||||||
import 'package:cw_core/transaction_direction.dart';
|
import 'package:cw_core/transaction_direction.dart';
|
||||||
import 'package:cw_core/utils/print_verbose.dart';
|
import 'package:cw_core/utils/print_verbose.dart';
|
||||||
|
import 'package:cw_core/pathForWallet.dart';
|
||||||
|
import 'package:cw_core/wallet_type.dart';
|
||||||
import 'package:cw_decred/amount_format.dart';
|
import 'package:cw_decred/amount_format.dart';
|
||||||
import 'package:cw_decred/pending_transaction.dart';
|
import 'package:cw_decred/pending_transaction.dart';
|
||||||
import 'package:cw_decred/transaction_credentials.dart';
|
import 'package:cw_decred/transaction_credentials.dart';
|
||||||
|
@ -307,9 +309,10 @@ abstract class DecredWalletBase
|
||||||
persistantPeer = addr;
|
persistantPeer = addr;
|
||||||
await _libwallet.closeWallet(walletInfo.name);
|
await _libwallet.closeWallet(walletInfo.name);
|
||||||
final network = isTestnet ? "testnet" : "mainnet";
|
final network = isTestnet ? "testnet" : "mainnet";
|
||||||
|
final dirPath = await pathForWalletDir(name: walletInfo.name, type: WalletType.decred);
|
||||||
final config = {
|
final config = {
|
||||||
"name": walletInfo.name,
|
"name": walletInfo.name,
|
||||||
"datadir": walletInfo.dirPath,
|
"datadir": dirPath,
|
||||||
"net": network,
|
"net": network,
|
||||||
"unsyncedaddrs": true,
|
"unsyncedaddrs": true,
|
||||||
};
|
};
|
||||||
|
@ -605,22 +608,22 @@ abstract class DecredWalletBase
|
||||||
|
|
||||||
final sourceDir = Directory(currentDirPath);
|
final sourceDir = Directory(currentDirPath);
|
||||||
final targetDir = Directory(newDirPath);
|
final targetDir = Directory(newDirPath);
|
||||||
|
|
||||||
if (!targetDir.existsSync()) {
|
if (!targetDir.existsSync()) {
|
||||||
await targetDir.create(recursive: true);
|
await targetDir.create(recursive: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
await for (final entity in sourceDir.list(recursive: true)) {
|
await for (final entity in sourceDir.list(recursive: true)) {
|
||||||
final relativePath = entity.path.substring(sourceDir.path.length+1);
|
final relativePath = entity.path.substring(sourceDir.path.length + 1);
|
||||||
final targetPath = p.join(targetDir.path, relativePath);
|
final targetPath = p.join(targetDir.path, relativePath);
|
||||||
|
|
||||||
if (entity is File) {
|
if (entity is File) {
|
||||||
await entity.rename(targetPath);
|
await entity.rename(targetPath);
|
||||||
} else if (entity is Directory) {
|
} else if (entity is Directory) {
|
||||||
await Directory(targetPath).create(recursive: true);
|
await Directory(targetPath).create(recursive: true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await sourceDir.delete(recursive: true);
|
await sourceDir.delete(recursive: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@ import 'package:cw_core/wallet_service.dart';
|
||||||
import 'package:cw_core/pathForWallet.dart';
|
import 'package:cw_core/pathForWallet.dart';
|
||||||
import 'package:cw_core/wallet_info.dart';
|
import 'package:cw_core/wallet_info.dart';
|
||||||
import 'package:cw_core/wallet_type.dart';
|
import 'package:cw_core/wallet_type.dart';
|
||||||
|
import 'package:path/path.dart';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:cw_core/unspent_coins_info.dart';
|
import 'package:cw_core/unspent_coins_info.dart';
|
||||||
|
@ -57,42 +58,93 @@ class DecredWalletService extends WalletService<
|
||||||
@override
|
@override
|
||||||
Future<DecredWallet> create(DecredNewWalletCredentials credentials, {bool? isTestnet}) async {
|
Future<DecredWallet> create(DecredNewWalletCredentials credentials, {bool? isTestnet}) async {
|
||||||
await this.init();
|
await this.init();
|
||||||
|
final dirPath = await pathForWalletDir(name: credentials.walletInfo!.name, type: getType());
|
||||||
|
final network = isTestnet == true ? testnet : mainnet;
|
||||||
final config = {
|
final config = {
|
||||||
"name": credentials.walletInfo!.name,
|
"name": credentials.walletInfo!.name,
|
||||||
"datadir": credentials.walletInfo!.dirPath,
|
"datadir": dirPath,
|
||||||
"pass": credentials.password!,
|
"pass": credentials.password!,
|
||||||
"net": isTestnet == true ? testnet : mainnet,
|
"net": network,
|
||||||
"unsyncedaddrs": true,
|
"unsyncedaddrs": true,
|
||||||
};
|
};
|
||||||
await libwallet!.createWallet(jsonEncode(config));
|
await libwallet!.createWallet(jsonEncode(config));
|
||||||
final di = DerivationInfo(
|
final di = DerivationInfo(
|
||||||
derivationPath: isTestnet == true ? seedRestorePathTestnet : seedRestorePath);
|
derivationPath: isTestnet == true ? seedRestorePathTestnet : seedRestorePath);
|
||||||
credentials.walletInfo!.derivationInfo = di;
|
credentials.walletInfo!.derivationInfo = di;
|
||||||
|
credentials.walletInfo!.network = network;
|
||||||
|
// ios will move our wallet directory when updating. Since we must
|
||||||
|
// recalculate the new path every time we open the wallet, ensure this path
|
||||||
|
// is not used. An older wallet will have a directory here which is a
|
||||||
|
// condition for moving the wallet when opening, so this must be kept blank
|
||||||
|
// going forward.
|
||||||
|
credentials.walletInfo!.dirPath = "";
|
||||||
|
credentials.walletInfo!.path = "";
|
||||||
final wallet = DecredWallet(credentials.walletInfo!, credentials.password!,
|
final wallet = DecredWallet(credentials.walletInfo!, credentials.password!,
|
||||||
this.unspentCoinsInfoSource, libwallet!, closeLibwallet);
|
this.unspentCoinsInfoSource, libwallet!, closeLibwallet);
|
||||||
await wallet.init();
|
await wallet.init();
|
||||||
return wallet;
|
return wallet;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void copyDirectorySync(Directory source, Directory destination) {
|
||||||
|
/// create destination folder if not exist
|
||||||
|
if (!destination.existsSync()) {
|
||||||
|
destination.createSync(recursive: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// get all files from source (recursive: false is important here)
|
||||||
|
source.listSync(recursive: false).forEach((entity) {
|
||||||
|
final newPath = destination.path + Platform.pathSeparator + basename(entity.path);
|
||||||
|
if (entity is File) {
|
||||||
|
entity.rename(newPath);
|
||||||
|
} else if (entity is Directory) {
|
||||||
|
copyDirectorySync(entity, Directory(newPath));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> moveWallet(String fromPath, String toPath) async {
|
||||||
|
final oldWalletDir = new Directory(fromPath);
|
||||||
|
final newWalletDir = new Directory(toPath);
|
||||||
|
copyDirectorySync(oldWalletDir, newWalletDir);
|
||||||
|
// It would be ideal to delete the old directory here, but ios will error
|
||||||
|
// sometimes with "OS Error: No such file or directory, errno = 2" even
|
||||||
|
// after checking if it exists.
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<DecredWallet> openWallet(String name, String password) async {
|
Future<DecredWallet> openWallet(String name, String password) async {
|
||||||
final walletInfo = walletInfoSource.values
|
final walletInfo = walletInfoSource.values
|
||||||
.firstWhereOrNull((info) => info.id == WalletBase.idFor(name, getType()))!;
|
.firstWhereOrNull((info) => info.id == WalletBase.idFor(name, getType()))!;
|
||||||
final network = walletInfo.derivationInfo?.derivationPath == seedRestorePathTestnet ||
|
if (walletInfo.network == null || walletInfo.network == "") {
|
||||||
walletInfo.derivationInfo?.derivationPath == pubkeyRestorePathTestnet
|
walletInfo.network = walletInfo.derivationInfo?.derivationPath == seedRestorePathTestnet ||
|
||||||
? testnet
|
walletInfo.derivationInfo?.derivationPath == pubkeyRestorePathTestnet
|
||||||
: mainnet;
|
? testnet
|
||||||
|
: mainnet;
|
||||||
|
}
|
||||||
|
|
||||||
await this.init();
|
await this.init();
|
||||||
final walletDirExists = Directory(walletInfo.dirPath).existsSync();
|
|
||||||
if (!walletDirExists) {
|
// Cake wallet version 4.27.0 and earlier gave a wallet dir that did not
|
||||||
walletInfo.dirPath = await pathForWalletDir(name: name, type: getType());
|
// match the name. Move those to the correct place.
|
||||||
|
final dirPath = await pathForWalletDir(name: name, type: getType());
|
||||||
|
if (walletInfo.path != "") {
|
||||||
|
// On ios the stored dir no longer exists. We can only trust the basename.
|
||||||
|
// dirPath may already be updated and lost the basename, so look at path.
|
||||||
|
final randomBasename = basename(walletInfo.path);
|
||||||
|
final oldDir = await pathForWalletDir(name: randomBasename, type: getType());
|
||||||
|
if (oldDir != dirPath) {
|
||||||
|
await this.moveWallet(oldDir, dirPath);
|
||||||
|
}
|
||||||
|
// Clear the path so this does not trigger again.
|
||||||
|
walletInfo.dirPath = "";
|
||||||
|
walletInfo.path = "";
|
||||||
|
await walletInfo.save();
|
||||||
}
|
}
|
||||||
|
|
||||||
final config = {
|
final config = {
|
||||||
"name": walletInfo.name,
|
"name": name,
|
||||||
"datadir": walletInfo.dirPath,
|
"datadir": dirPath,
|
||||||
"net": network,
|
"net": walletInfo.network,
|
||||||
"unsyncedaddrs": true,
|
"unsyncedaddrs": true,
|
||||||
};
|
};
|
||||||
await libwallet!.loadWallet(jsonEncode(config));
|
await libwallet!.loadWallet(jsonEncode(config));
|
||||||
|
@ -127,12 +179,11 @@ class DecredWalletService extends WalletService<
|
||||||
|
|
||||||
await currentWallet.renameWalletFiles(newName);
|
await currentWallet.renameWalletFiles(newName);
|
||||||
|
|
||||||
final newDirPath = await pathForWalletDir(name: newName, type: getType());
|
|
||||||
final newWalletInfo = currentWalletInfo;
|
final newWalletInfo = currentWalletInfo;
|
||||||
newWalletInfo.id = WalletBase.idFor(newName, getType());
|
newWalletInfo.id = WalletBase.idFor(newName, getType());
|
||||||
newWalletInfo.name = newName;
|
newWalletInfo.name = newName;
|
||||||
newWalletInfo.dirPath = newDirPath;
|
newWalletInfo.dirPath = "";
|
||||||
newWalletInfo.network = network;
|
newWalletInfo.path = "";
|
||||||
|
|
||||||
await walletInfoSource.put(currentWalletInfo.key, newWalletInfo);
|
await walletInfoSource.put(currentWalletInfo.key, newWalletInfo);
|
||||||
}
|
}
|
||||||
|
@ -141,18 +192,23 @@ class DecredWalletService extends WalletService<
|
||||||
Future<DecredWallet> restoreFromSeed(DecredRestoreWalletFromSeedCredentials credentials,
|
Future<DecredWallet> restoreFromSeed(DecredRestoreWalletFromSeedCredentials credentials,
|
||||||
{bool? isTestnet}) async {
|
{bool? isTestnet}) async {
|
||||||
await this.init();
|
await this.init();
|
||||||
|
final network = isTestnet == true ? testnet : mainnet;
|
||||||
|
final dirPath = await pathForWalletDir(name: credentials.walletInfo!.name, type: getType());
|
||||||
final config = {
|
final config = {
|
||||||
"name": credentials.walletInfo!.name,
|
"name": credentials.walletInfo!.name,
|
||||||
"datadir": credentials.walletInfo!.dirPath,
|
"datadir": dirPath,
|
||||||
"pass": credentials.password!,
|
"pass": credentials.password!,
|
||||||
"mnemonic": credentials.mnemonic,
|
"mnemonic": credentials.mnemonic,
|
||||||
"net": isTestnet == true ? testnet : mainnet,
|
"net": network,
|
||||||
"unsyncedaddrs": true,
|
"unsyncedaddrs": true,
|
||||||
};
|
};
|
||||||
await libwallet!.createWallet(jsonEncode(config));
|
await libwallet!.createWallet(jsonEncode(config));
|
||||||
final di = DerivationInfo(
|
final di = DerivationInfo(
|
||||||
derivationPath: isTestnet == true ? seedRestorePathTestnet : seedRestorePath);
|
derivationPath: isTestnet == true ? seedRestorePathTestnet : seedRestorePath);
|
||||||
credentials.walletInfo!.derivationInfo = di;
|
credentials.walletInfo!.derivationInfo = di;
|
||||||
|
credentials.walletInfo!.network = network;
|
||||||
|
credentials.walletInfo!.dirPath = "";
|
||||||
|
credentials.walletInfo!.path = "";
|
||||||
final wallet = DecredWallet(credentials.walletInfo!, credentials.password!,
|
final wallet = DecredWallet(credentials.walletInfo!, credentials.password!,
|
||||||
this.unspentCoinsInfoSource, libwallet!, closeLibwallet);
|
this.unspentCoinsInfoSource, libwallet!, closeLibwallet);
|
||||||
await wallet.init();
|
await wallet.init();
|
||||||
|
@ -165,17 +221,22 @@ class DecredWalletService extends WalletService<
|
||||||
Future<DecredWallet> restoreFromKeys(DecredRestoreWalletFromPubkeyCredentials credentials,
|
Future<DecredWallet> restoreFromKeys(DecredRestoreWalletFromPubkeyCredentials credentials,
|
||||||
{bool? isTestnet}) async {
|
{bool? isTestnet}) async {
|
||||||
await this.init();
|
await this.init();
|
||||||
|
final network = isTestnet == true ? testnet : mainnet;
|
||||||
|
final dirPath = await pathForWalletDir(name: credentials.walletInfo!.name, type: getType());
|
||||||
final config = {
|
final config = {
|
||||||
"name": credentials.walletInfo!.name,
|
"name": credentials.walletInfo!.name,
|
||||||
"datadir": credentials.walletInfo!.dirPath,
|
"datadir": dirPath,
|
||||||
"pubkey": credentials.pubkey,
|
"pubkey": credentials.pubkey,
|
||||||
"net": isTestnet == true ? testnet : mainnet,
|
"net": network,
|
||||||
"unsyncedaddrs": true,
|
"unsyncedaddrs": true,
|
||||||
};
|
};
|
||||||
await libwallet!.createWatchOnlyWallet(jsonEncode(config));
|
await libwallet!.createWatchOnlyWallet(jsonEncode(config));
|
||||||
final di = DerivationInfo(
|
final di = DerivationInfo(
|
||||||
derivationPath: isTestnet == true ? pubkeyRestorePathTestnet : pubkeyRestorePath);
|
derivationPath: isTestnet == true ? pubkeyRestorePathTestnet : pubkeyRestorePath);
|
||||||
credentials.walletInfo!.derivationInfo = di;
|
credentials.walletInfo!.derivationInfo = di;
|
||||||
|
credentials.walletInfo!.network = network;
|
||||||
|
credentials.walletInfo!.dirPath = "";
|
||||||
|
credentials.walletInfo!.path = "";
|
||||||
final wallet = DecredWallet(credentials.walletInfo!, credentials.password!,
|
final wallet = DecredWallet(credentials.walletInfo!, credentials.password!,
|
||||||
this.unspentCoinsInfoSource, libwallet!, closeLibwallet);
|
this.unspentCoinsInfoSource, libwallet!, closeLibwallet);
|
||||||
await wallet.init();
|
await wallet.init();
|
||||||
|
|
|
@ -666,11 +666,21 @@ packages:
|
||||||
socks5_proxy:
|
socks5_proxy:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: socks5_proxy
|
path: "."
|
||||||
sha256: "616818a0ea1064a4823b53c9f7eaf8da64ed82dcd51ed71371c7e54751ed5053"
|
ref: "27ad7c2efae8d7460325c74b90f660085cbd0685"
|
||||||
url: "https://pub.dev"
|
resolved-ref: "27ad7c2efae8d7460325c74b90f660085cbd0685"
|
||||||
source: hosted
|
url: "https://github.com/LacticWhale/socks_dart"
|
||||||
version: "1.0.6"
|
source: git
|
||||||
|
version: "2.1.0"
|
||||||
|
socks_socket:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
path: "."
|
||||||
|
ref: e6232c53c1595469931ababa878759a067c02e94
|
||||||
|
resolved-ref: e6232c53c1595469931ababa878759a067c02e94
|
||||||
|
url: "https://github.com/sneurlax/socks_socket"
|
||||||
|
source: git
|
||||||
|
version: "1.1.1"
|
||||||
source_gen:
|
source_gen:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -751,6 +761,15 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.2"
|
version: "1.0.2"
|
||||||
|
tor_binary:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
path: "."
|
||||||
|
ref: cb811c610871a9517d47134b87c2f590c15c96c5
|
||||||
|
resolved-ref: cb811c610871a9517d47134b87c2f590c15c96c5
|
||||||
|
url: "https://github.com/MrCyjaneK/flutter-tor_binary"
|
||||||
|
source: git
|
||||||
|
version: "4.7.14"
|
||||||
tuple:
|
tuple:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
121
cw_ethereum/lib/deuro/deuro_savings.dart
Normal file
121
cw_ethereum/lib/deuro/deuro_savings.dart
Normal file
|
@ -0,0 +1,121 @@
|
||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:crypto/crypto.dart';
|
||||||
|
import 'package:cw_core/crypto_currency.dart';
|
||||||
|
import 'package:cw_ethereum/deuro/deuro_savings_contract.dart';
|
||||||
|
import 'package:cw_ethereum/ethereum_wallet.dart';
|
||||||
|
import 'package:cw_evm/contract/erc20.dart';
|
||||||
|
import 'package:cw_evm/evm_chain_transaction_priority.dart';
|
||||||
|
import 'package:cw_evm/pending_evm_chain_transaction.dart';
|
||||||
|
import 'package:web3dart/web3dart.dart';
|
||||||
|
|
||||||
|
const String savingsGatewayAddress =
|
||||||
|
"0x073493d73258C4BEb6542e8dd3e1b2891C972303";
|
||||||
|
|
||||||
|
const String dEuroAddress = "0xbA3f535bbCcCcA2A154b573Ca6c5A49BAAE0a3ea";
|
||||||
|
|
||||||
|
class DEuro {
|
||||||
|
final SavingsGateway _savingsGateway;
|
||||||
|
final ERC20 _dEuro;
|
||||||
|
final EthereumWallet _wallet;
|
||||||
|
|
||||||
|
DEuro(EthereumWallet wallet)
|
||||||
|
: _wallet = wallet,
|
||||||
|
_savingsGateway = _getSavingsGateway(wallet.getWeb3Client()!),
|
||||||
|
_dEuro = _getDEuroToken(wallet.getWeb3Client()!);
|
||||||
|
|
||||||
|
static SavingsGateway _getSavingsGateway(Web3Client client) => SavingsGateway(
|
||||||
|
address: EthereumAddress.fromHex(savingsGatewayAddress),
|
||||||
|
client: client,
|
||||||
|
);
|
||||||
|
|
||||||
|
static ERC20 _getDEuroToken(Web3Client client) => ERC20(
|
||||||
|
address: EthereumAddress.fromHex(dEuroAddress),
|
||||||
|
client: client,
|
||||||
|
);
|
||||||
|
|
||||||
|
final frontendCode =
|
||||||
|
Uint8List.fromList(sha256.convert(utf8.encode("wallet")).bytes);
|
||||||
|
|
||||||
|
EthereumAddress get _address =>
|
||||||
|
EthereumAddress.fromHex(_wallet.walletAddresses.primaryAddress);
|
||||||
|
|
||||||
|
Future<BigInt> get savingsBalance async =>
|
||||||
|
(await _savingsGateway.savings(accountOwner: _address)).saved;
|
||||||
|
|
||||||
|
Future<BigInt> get accruedInterest =>
|
||||||
|
_savingsGateway.accruedInterest(accountOwner: _address);
|
||||||
|
|
||||||
|
Future<BigInt> get interestRate => _savingsGateway.currentRatePPM();
|
||||||
|
|
||||||
|
Future<BigInt> get approvedBalance =>
|
||||||
|
_dEuro.allowance(_address, _savingsGateway.self.address);
|
||||||
|
|
||||||
|
Future<PendingEVMChainTransaction> depositSavings(
|
||||||
|
BigInt amount, EVMChainTransactionPriority priority) async {
|
||||||
|
final signedTransaction = await _savingsGateway.save(
|
||||||
|
(amount: amount, frontendCode: frontendCode),
|
||||||
|
credentials: _wallet.evmChainPrivateKey,
|
||||||
|
);
|
||||||
|
|
||||||
|
final fee = await _wallet.calculateActualEstimatedFeeForCreateTransaction(
|
||||||
|
amount: amount,
|
||||||
|
contractAddress: _savingsGateway.self.address.hexEip55,
|
||||||
|
receivingAddressHex: _savingsGateway.self.address.hexEip55,
|
||||||
|
priority: priority,
|
||||||
|
data: _savingsGateway.self.abi.functions[17]
|
||||||
|
.encodeCall([amount, frontendCode]),
|
||||||
|
);
|
||||||
|
|
||||||
|
final sendTransaction =
|
||||||
|
() => _wallet.getWeb3Client()!.sendRawTransaction(signedTransaction);
|
||||||
|
|
||||||
|
return PendingEVMChainTransaction(
|
||||||
|
sendTransaction: sendTransaction,
|
||||||
|
signedTransaction: signedTransaction,
|
||||||
|
fee: BigInt.from(fee.estimatedGasFee),
|
||||||
|
amount: amount.toString(),
|
||||||
|
exponent: 18);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<PendingEVMChainTransaction> withdrawSavings(
|
||||||
|
BigInt amount, EVMChainTransactionPriority priority) async {
|
||||||
|
final signedTransaction = await _savingsGateway.withdraw(
|
||||||
|
(target: _address, amount: amount, frontendCode: frontendCode),
|
||||||
|
credentials: _wallet.evmChainPrivateKey,
|
||||||
|
);
|
||||||
|
|
||||||
|
final fee = await _wallet.calculateActualEstimatedFeeForCreateTransaction(
|
||||||
|
amount: amount,
|
||||||
|
contractAddress: _savingsGateway.self.address.hexEip55,
|
||||||
|
receivingAddressHex: _savingsGateway.self.address.hexEip55,
|
||||||
|
priority: priority,
|
||||||
|
data: _savingsGateway.self.abi.functions[17]
|
||||||
|
.encodeCall([amount, frontendCode]),
|
||||||
|
);
|
||||||
|
|
||||||
|
final sendTransaction =
|
||||||
|
() => _wallet.getWeb3Client()!.sendRawTransaction(signedTransaction);
|
||||||
|
|
||||||
|
return PendingEVMChainTransaction(
|
||||||
|
sendTransaction: sendTransaction,
|
||||||
|
signedTransaction: signedTransaction,
|
||||||
|
fee: BigInt.from(fee.estimatedGasFee),
|
||||||
|
amount: amount.toString(),
|
||||||
|
exponent: 18);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set an infinite approval to save gas in the future
|
||||||
|
Future<PendingEVMChainTransaction> enableSavings(
|
||||||
|
EVMChainTransactionPriority priority) async =>
|
||||||
|
(await _wallet.createApprovalTransaction(
|
||||||
|
BigInt.parse(
|
||||||
|
'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
|
||||||
|
radix: 16,
|
||||||
|
),
|
||||||
|
_savingsGateway.self.address.hexEip55,
|
||||||
|
CryptoCurrency.deuro,
|
||||||
|
priority,
|
||||||
|
)) as PendingEVMChainTransaction;
|
||||||
|
}
|
543
cw_ethereum/lib/deuro/deuro_savings_contract.dart
Normal file
543
cw_ethereum/lib/deuro/deuro_savings_contract.dart
Normal file
File diff suppressed because one or more lines are too long
|
@ -19,7 +19,7 @@ class EthereumClient extends EVMChainClient {
|
||||||
Future<List<EVMChainTransactionModel>> fetchTransactions(String address,
|
Future<List<EVMChainTransactionModel>> fetchTransactions(String address,
|
||||||
{String? contractAddress}) async {
|
{String? contractAddress}) async {
|
||||||
try {
|
try {
|
||||||
final response = await httpClient.get(Uri.https("api.etherscan.io", "/v2/api", {
|
final response = await client.get(Uri.https("api.etherscan.io", "/v2/api", {
|
||||||
"chainid": "$chainId",
|
"chainid": "$chainId",
|
||||||
"module": "account",
|
"module": "account",
|
||||||
"action": contractAddress != null ? "tokentx" : "txlist",
|
"action": contractAddress != null ? "tokentx" : "txlist",
|
||||||
|
@ -51,7 +51,7 @@ class EthereumClient extends EVMChainClient {
|
||||||
@override
|
@override
|
||||||
Future<List<EVMChainTransactionModel>> fetchInternalTransactions(String address) async {
|
Future<List<EVMChainTransactionModel>> fetchInternalTransactions(String address) async {
|
||||||
try {
|
try {
|
||||||
final response = await httpClient.get(Uri.https("api.etherscan.io", "/v2/api", {
|
final response = await client.get(Uri.https("api.etherscan.io", "/v2/api", {
|
||||||
"chainid": "$chainId",
|
"chainid": "$chainId",
|
||||||
"module": "account",
|
"module": "account",
|
||||||
"action": "txlistinternal",
|
"action": "txlistinternal",
|
||||||
|
|
|
@ -6,7 +6,7 @@ author: Cake Wallet
|
||||||
homepage: https://cakewallet.com
|
homepage: https://cakewallet.com
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: '>=2.18.2 <3.0.0'
|
sdk: ^3.5.0
|
||||||
flutter: ">=1.17.0"
|
flutter: ">=1.17.0"
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
|
|
|
@ -5,6 +5,7 @@ import 'dart:developer';
|
||||||
import 'package:cw_core/crypto_currency.dart';
|
import 'package:cw_core/crypto_currency.dart';
|
||||||
import 'package:cw_core/erc20_token.dart';
|
import 'package:cw_core/erc20_token.dart';
|
||||||
import 'package:cw_core/node.dart';
|
import 'package:cw_core/node.dart';
|
||||||
|
import 'package:cw_core/utils/proxy_wrapper.dart';
|
||||||
import 'package:cw_evm/evm_chain_transaction_model.dart';
|
import 'package:cw_evm/evm_chain_transaction_model.dart';
|
||||||
import 'package:cw_evm/evm_chain_transaction_priority.dart';
|
import 'package:cw_evm/evm_chain_transaction_priority.dart';
|
||||||
import 'package:cw_evm/evm_erc20_balance.dart';
|
import 'package:cw_evm/evm_erc20_balance.dart';
|
||||||
|
@ -12,13 +13,12 @@ import 'package:cw_evm/pending_evm_chain_transaction.dart';
|
||||||
import 'package:cw_evm/.secrets.g.dart' as secrets;
|
import 'package:cw_evm/.secrets.g.dart' as secrets;
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:hex/hex.dart' as hex;
|
import 'package:hex/hex.dart' as hex;
|
||||||
import 'package:http/http.dart';
|
|
||||||
import 'package:web3dart/web3dart.dart';
|
import 'package:web3dart/web3dart.dart';
|
||||||
|
|
||||||
import 'contract/erc20.dart';
|
import 'contract/erc20.dart';
|
||||||
|
|
||||||
abstract class EVMChainClient {
|
abstract class EVMChainClient {
|
||||||
final httpClient = Client();
|
late final client = ProxyWrapper().getHttpIOClient();
|
||||||
Web3Client? _client;
|
Web3Client? _client;
|
||||||
|
|
||||||
//! To be overridden by all child classes
|
//! To be overridden by all child classes
|
||||||
|
@ -47,7 +47,7 @@ abstract class EVMChainClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
_client =
|
_client =
|
||||||
Web3Client(isModifiedNodeUri ? rpcUri!.toString() : node.uri.toString(), httpClient);
|
Web3Client(isModifiedNodeUri ? rpcUri!.toString() : node.uri.toString(), client);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -76,7 +76,7 @@ abstract class EVMChainClient {
|
||||||
Future<int> getGasUnitPrice() async {
|
Future<int> getGasUnitPrice() async {
|
||||||
try {
|
try {
|
||||||
final gasPrice = await _client!.getGasPrice();
|
final gasPrice = await _client!.getGasPrice();
|
||||||
|
|
||||||
return gasPrice.getInWei.toInt();
|
return gasPrice.getInWei.toInt();
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -101,6 +101,7 @@ abstract class EVMChainClient {
|
||||||
String? contractAddress,
|
String? contractAddress,
|
||||||
EtherAmount? gasPrice,
|
EtherAmount? gasPrice,
|
||||||
EtherAmount? maxFeePerGas,
|
EtherAmount? maxFeePerGas,
|
||||||
|
Uint8List? data,
|
||||||
}) async {
|
}) async {
|
||||||
try {
|
try {
|
||||||
if (contractAddress == null) {
|
if (contractAddress == null) {
|
||||||
|
@ -124,7 +125,7 @@ abstract class EVMChainClient {
|
||||||
final gasEstimate = await _client!.estimateGas(
|
final gasEstimate = await _client!.estimateGas(
|
||||||
sender: senderAddress,
|
sender: senderAddress,
|
||||||
to: EthereumAddress.fromHex(contractAddress),
|
to: EthereumAddress.fromHex(contractAddress),
|
||||||
data: transfer.encodeCall([
|
data: data ?? transfer.encodeCall([
|
||||||
toAddress,
|
toAddress,
|
||||||
value.getInWei,
|
value.getInWei,
|
||||||
]),
|
]),
|
||||||
|
@ -137,6 +138,21 @@ abstract class EVMChainClient {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Uint8List getEncodedDataForApprovalTransaction({
|
||||||
|
required EthereumAddress toAddress,
|
||||||
|
required EtherAmount value,
|
||||||
|
required EthereumAddress contractAddress,
|
||||||
|
}) {
|
||||||
|
final contract = DeployedContract(ethereumContractAbi, contractAddress);
|
||||||
|
|
||||||
|
final approve = contract.function('approve');
|
||||||
|
|
||||||
|
return approve.encodeCall([
|
||||||
|
toAddress,
|
||||||
|
value.getInWei,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
Future<PendingEVMChainTransaction> signTransaction({
|
Future<PendingEVMChainTransaction> signTransaction({
|
||||||
required Credentials privateKey,
|
required Credentials privateKey,
|
||||||
required String toAddress,
|
required String toAddress,
|
||||||
|
@ -198,6 +214,50 @@ abstract class EVMChainClient {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<PendingEVMChainTransaction> signApprovalTransaction({
|
||||||
|
required Credentials privateKey,
|
||||||
|
required String spender,
|
||||||
|
required BigInt amount,
|
||||||
|
required BigInt gasFee,
|
||||||
|
required int estimatedGasUnits,
|
||||||
|
required int maxFeePerGas,
|
||||||
|
required EVMChainTransactionPriority priority,
|
||||||
|
required int exponent,
|
||||||
|
required String contractAddress,
|
||||||
|
}) async {
|
||||||
|
|
||||||
|
final Transaction transaction = createTransaction(
|
||||||
|
from: privateKey.address,
|
||||||
|
to: EthereumAddress.fromHex(contractAddress),
|
||||||
|
maxPriorityFeePerGas: EtherAmount.fromInt(EtherUnit.gwei, priority.tip),
|
||||||
|
amount: EtherAmount.zero(),
|
||||||
|
maxGas: estimatedGasUnits,
|
||||||
|
maxFeePerGas: EtherAmount.fromInt(EtherUnit.wei, maxFeePerGas),
|
||||||
|
);
|
||||||
|
|
||||||
|
final erc20 = ERC20(
|
||||||
|
client: _client!,
|
||||||
|
address: EthereumAddress.fromHex(contractAddress),
|
||||||
|
chainId: chainId,
|
||||||
|
);
|
||||||
|
|
||||||
|
final signedTransaction = await erc20.approve(
|
||||||
|
EthereumAddress.fromHex(spender),
|
||||||
|
amount,
|
||||||
|
credentials: privateKey,
|
||||||
|
transaction: transaction,
|
||||||
|
);
|
||||||
|
|
||||||
|
return PendingEVMChainTransaction(
|
||||||
|
signedTransaction: prepareSignedTransactionForSending(signedTransaction),
|
||||||
|
amount: amount.toString(),
|
||||||
|
fee: gasFee,
|
||||||
|
sendTransaction: () => sendTransaction(signedTransaction),
|
||||||
|
exponent: exponent,
|
||||||
|
isInfiniteApproval: amount.toRadixString(16) == 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Transaction createTransaction({
|
Transaction createTransaction({
|
||||||
required EthereumAddress from,
|
required EthereumAddress from,
|
||||||
required EthereumAddress to,
|
required EthereumAddress to,
|
||||||
|
@ -293,7 +353,7 @@ abstract class EVMChainClient {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
final response = await httpClient.get(
|
final response = await client.get(
|
||||||
uri,
|
uri,
|
||||||
headers: {
|
headers: {
|
||||||
"Accept": "application/json",
|
"Accept": "application/json",
|
||||||
|
|
|
@ -2,6 +2,7 @@ import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:bip32/bip32.dart' as bip32;
|
import 'package:bip32/bip32.dart' as bip32;
|
||||||
import 'package:bip39/bip39.dart' as bip39;
|
import 'package:bip39/bip39.dart' as bip39;
|
||||||
|
@ -255,6 +256,7 @@ abstract class EVMChainWalletBase
|
||||||
required String? contractAddress,
|
required String? contractAddress,
|
||||||
required String receivingAddressHex,
|
required String receivingAddressHex,
|
||||||
required TransactionPriority priority,
|
required TransactionPriority priority,
|
||||||
|
Uint8List? data,
|
||||||
}) async {
|
}) async {
|
||||||
try {
|
try {
|
||||||
if (priority is EVMChainTransactionPriority) {
|
if (priority is EVMChainTransactionPriority) {
|
||||||
|
@ -276,6 +278,7 @@ abstract class EVMChainWalletBase
|
||||||
gasPrice: EtherAmount.fromInt(EtherUnit.wei, gasPrice),
|
gasPrice: EtherAmount.fromInt(EtherUnit.wei, gasPrice),
|
||||||
toAddress: EthereumAddress.fromHex(receivingAddressHex),
|
toAddress: EthereumAddress.fromHex(receivingAddressHex),
|
||||||
maxFeePerGas: EtherAmount.fromInt(EtherUnit.wei, maxFeePerGas),
|
maxFeePerGas: EtherAmount.fromInt(EtherUnit.wei, maxFeePerGas),
|
||||||
|
data: data,
|
||||||
);
|
);
|
||||||
|
|
||||||
final totalGasFee = estimatedGas * maxFeePerGas;
|
final totalGasFee = estimatedGas * maxFeePerGas;
|
||||||
|
@ -478,6 +481,43 @@ abstract class EVMChainWalletBase
|
||||||
return pendingEVMChainTransaction;
|
return pendingEVMChainTransaction;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<PendingTransaction> createApprovalTransaction(
|
||||||
|
BigInt amount,
|
||||||
|
String spender,
|
||||||
|
CryptoCurrency token,
|
||||||
|
EVMChainTransactionPriority priority) async {
|
||||||
|
final CryptoCurrency transactionCurrency =
|
||||||
|
balance.keys.firstWhere((element) => element.title == token.title);
|
||||||
|
assert(transactionCurrency is Erc20Token);
|
||||||
|
|
||||||
|
final data = _client.getEncodedDataForApprovalTransaction(
|
||||||
|
contractAddress: EthereumAddress.fromHex(
|
||||||
|
(transactionCurrency as Erc20Token).contractAddress),
|
||||||
|
value: EtherAmount.fromBigInt(EtherUnit.wei, amount),
|
||||||
|
toAddress: EthereumAddress.fromHex(spender),
|
||||||
|
);
|
||||||
|
|
||||||
|
final gasFeesModel = await calculateActualEstimatedFeeForCreateTransaction(
|
||||||
|
amount: amount,
|
||||||
|
receivingAddressHex: spender,
|
||||||
|
priority: priority,
|
||||||
|
contractAddress: transactionCurrency.contractAddress,
|
||||||
|
data: data,
|
||||||
|
);
|
||||||
|
|
||||||
|
return _client.signApprovalTransaction(
|
||||||
|
privateKey: _evmChainPrivateKey,
|
||||||
|
spender: spender,
|
||||||
|
amount: amount,
|
||||||
|
priority: priority,
|
||||||
|
gasFee: BigInt.from(gasFeesModel.estimatedGasFee),
|
||||||
|
maxFeePerGas: gasFeesModel.maxFeePerGas,
|
||||||
|
estimatedGasUnits: gasFeesModel.estimatedGasUnits,
|
||||||
|
exponent: transactionCurrency.decimal,
|
||||||
|
contractAddress: transactionCurrency.contractAddress,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> _updateTransactions() async {
|
Future<void> _updateTransactions() async {
|
||||||
try {
|
try {
|
||||||
if (_isTransactionUpdating) {
|
if (_isTransactionUpdating) {
|
||||||
|
|
|
@ -11,6 +11,7 @@ class PendingEVMChainTransaction with PendingTransaction {
|
||||||
final BigInt fee;
|
final BigInt fee;
|
||||||
final String amount;
|
final String amount;
|
||||||
final int exponent;
|
final int exponent;
|
||||||
|
final bool isInfiniteApproval;
|
||||||
|
|
||||||
PendingEVMChainTransaction({
|
PendingEVMChainTransaction({
|
||||||
required this.sendTransaction,
|
required this.sendTransaction,
|
||||||
|
@ -18,10 +19,12 @@ class PendingEVMChainTransaction with PendingTransaction {
|
||||||
required this.fee,
|
required this.fee,
|
||||||
required this.amount,
|
required this.amount,
|
||||||
required this.exponent,
|
required this.exponent,
|
||||||
|
this.isInfiniteApproval = false,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get amountFormatted {
|
String get amountFormatted {
|
||||||
|
if (isInfiniteApproval) return "∞";
|
||||||
final _amount = (BigInt.parse(amount) / BigInt.from(pow(10, exponent))).toString();
|
final _amount = (BigInt.parse(amount) / BigInt.from(pow(10, exponent))).toString();
|
||||||
return _amount.substring(0, min(10, _amount.length));
|
return _amount.substring(0, min(10, _amount.length));
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,7 @@ dependencies:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/cake-tech/ledger-flutter-plus-plugins
|
url: https://github.com/cake-tech/ledger-flutter-plus-plugins
|
||||||
path: packages/ledger-ethereum
|
path: packages/ledger-ethereum
|
||||||
|
ref: f4761cd5171d4c1e2e42fd3298261650539fb2db
|
||||||
|
|
||||||
dependency_overrides:
|
dependency_overrides:
|
||||||
web3dart:
|
web3dart:
|
||||||
|
|
|
@ -5,13 +5,11 @@ class PendingTransactionDescription {
|
||||||
required this.fee,
|
required this.fee,
|
||||||
required this.hash,
|
required this.hash,
|
||||||
required this.hex,
|
required this.hex,
|
||||||
required this.txKey,
|
|
||||||
required this.pointerAddress});
|
required this.pointerAddress});
|
||||||
|
|
||||||
final int amount;
|
final int amount;
|
||||||
final int fee;
|
final int fee;
|
||||||
final String hash;
|
final String hash;
|
||||||
final String hex;
|
final String hex;
|
||||||
final String txKey;
|
|
||||||
final int pointerAddress;
|
final int pointerAddress;
|
||||||
}
|
}
|
|
@ -1,3 +1,4 @@
|
||||||
|
import 'dart:async';
|
||||||
import 'dart:ffi';
|
import 'dart:ffi';
|
||||||
import 'dart:isolate';
|
import 'dart:isolate';
|
||||||
|
|
||||||
|
@ -194,14 +195,12 @@ Future<PendingTransactionDescription> createTransactionSync(
|
||||||
final rFee = pendingTx.fee();
|
final rFee = pendingTx.fee();
|
||||||
final rHash = pendingTx.txid('');
|
final rHash = pendingTx.txid('');
|
||||||
final rHex = pendingTx.hex('');
|
final rHex = pendingTx.hex('');
|
||||||
final rTxKey = rHash;
|
|
||||||
|
|
||||||
return PendingTransactionDescription(
|
return PendingTransactionDescription(
|
||||||
amount: rAmt,
|
amount: rAmt,
|
||||||
fee: rFee,
|
fee: rFee,
|
||||||
hash: rHash,
|
hash: rHash,
|
||||||
hex: rHex,
|
hex: rHex,
|
||||||
txKey: rTxKey,
|
|
||||||
pointerAddress: pendingTx.ffiAddress(),
|
pointerAddress: pendingTx.ffiAddress(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -246,7 +245,6 @@ Future<PendingTransactionDescription> createTransactionMultDest(
|
||||||
fee: tx.fee(),
|
fee: tx.fee(),
|
||||||
hash: tx.txid(''),
|
hash: tx.txid(''),
|
||||||
hex: tx.hex(''),
|
hex: tx.hex(''),
|
||||||
txKey: tx.txid(''),
|
|
||||||
pointerAddress: tx.ffiAddress(),
|
pointerAddress: tx.ffiAddress(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -263,6 +261,7 @@ Future<String?> commitTransaction({required Wallet2PendingTransaction tx, requir
|
||||||
filename: '',
|
filename: '',
|
||||||
overwrite: false,
|
overwrite: false,
|
||||||
);
|
);
|
||||||
|
return null;
|
||||||
});
|
});
|
||||||
|
|
||||||
String? error = (() {
|
String? error = (() {
|
||||||
|
@ -285,11 +284,12 @@ Future<String?> commitTransaction({required Wallet2PendingTransaction tx, requir
|
||||||
if (error != null && error != "no tx keys found for this txid") {
|
if (error != null && error != "no tx keys found for this txid") {
|
||||||
throw CreationTransactionException(message: error);
|
throw CreationTransactionException(message: error);
|
||||||
}
|
}
|
||||||
if (useUR) {
|
unawaited(() async {
|
||||||
return Future.value(txCommit as String?);
|
storeSync(force: true);
|
||||||
} else {
|
await Future.delayed(Duration(seconds: 5));
|
||||||
return Future.value(null);
|
storeSync(force: true);
|
||||||
}
|
}());
|
||||||
|
return Future.value(txCommit);
|
||||||
}
|
}
|
||||||
|
|
||||||
class Transaction {
|
class Transaction {
|
||||||
|
|
|
@ -146,7 +146,25 @@ int getUnlockedBalance({int accountIndex = 0}) =>
|
||||||
|
|
||||||
int getCurrentHeight() => currentWallet?.blockChainHeight() ?? 0;
|
int getCurrentHeight() => currentWallet?.blockChainHeight() ?? 0;
|
||||||
|
|
||||||
int getNodeHeightSync() => currentWallet?.daemonBlockChainHeight() ?? 0;
|
|
||||||
|
int cachedNodeHeight = 0;
|
||||||
|
bool isHeightRefreshing = false;
|
||||||
|
int getNodeHeightSync() {
|
||||||
|
if (isHeightRefreshing == false) {
|
||||||
|
(() async {
|
||||||
|
try {
|
||||||
|
isHeightRefreshing = true;
|
||||||
|
final wptrAddress = currentWallet!.ffiAddress();
|
||||||
|
cachedNodeHeight = await Isolate.run(() async {
|
||||||
|
return monero.Wallet_daemonBlockChainHeight(Pointer.fromAddress(wptrAddress));
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
isHeightRefreshing = false;
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
return cachedNodeHeight;
|
||||||
|
}
|
||||||
|
|
||||||
bool isConnectedSync() => currentWallet?.connected() != 0;
|
bool isConnectedSync() => currentWallet?.connected() != 0;
|
||||||
|
|
||||||
|
@ -202,7 +220,6 @@ Future<bool> setupNodeSync(
|
||||||
}
|
}
|
||||||
|
|
||||||
void startRefreshSync() {
|
void startRefreshSync() {
|
||||||
currentWallet!.refreshAsync();
|
|
||||||
currentWallet!.startRefresh();
|
currentWallet!.startRefresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -228,7 +245,7 @@ void storeSync({bool force = false}) async {
|
||||||
return monero.Wallet_synchronized(Pointer.fromAddress(addr));
|
return monero.Wallet_synchronized(Pointer.fromAddress(addr));
|
||||||
});
|
});
|
||||||
if (lastStorePointer == addr &&
|
if (lastStorePointer == addr &&
|
||||||
lastStoreHeight + 5000 > currentWallet!.blockChainHeight() &&
|
lastStoreHeight + 75000 > currentWallet!.blockChainHeight() &&
|
||||||
!synchronized &&
|
!synchronized &&
|
||||||
!force) {
|
!force) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -17,6 +17,7 @@ import 'package:cw_core/pending_transaction.dart';
|
||||||
import 'package:cw_core/sync_status.dart';
|
import 'package:cw_core/sync_status.dart';
|
||||||
import 'package:cw_core/transaction_direction.dart';
|
import 'package:cw_core/transaction_direction.dart';
|
||||||
import 'package:cw_core/unspent_coins_info.dart';
|
import 'package:cw_core/unspent_coins_info.dart';
|
||||||
|
import 'package:cw_core/utils/proxy_wrapper.dart';
|
||||||
import 'package:cw_core/utils/print_verbose.dart';
|
import 'package:cw_core/utils/print_verbose.dart';
|
||||||
import 'package:cw_core/wallet_base.dart';
|
import 'package:cw_core/wallet_base.dart';
|
||||||
import 'package:cw_core/wallet_info.dart';
|
import 'package:cw_core/wallet_info.dart';
|
||||||
|
@ -207,6 +208,14 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> connectToNode({required Node node}) async {
|
Future<void> connectToNode({required Node node}) async {
|
||||||
|
String socksProxy = node.socksProxyAddress ?? '';
|
||||||
|
printV("bootstrapped: ${CakeTor.instance.bootstrapped}");
|
||||||
|
printV(" enabled: ${CakeTor.instance.enabled}");
|
||||||
|
printV(" port: ${CakeTor.instance.port}");
|
||||||
|
printV(" started: ${CakeTor.instance.started}");
|
||||||
|
if (CakeTor.instance.enabled) {
|
||||||
|
socksProxy = "127.0.0.1:${CakeTor.instance.port}";
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
syncStatus = ConnectingSyncStatus();
|
syncStatus = ConnectingSyncStatus();
|
||||||
await monero_wallet.setupNodeSync(
|
await monero_wallet.setupNodeSync(
|
||||||
|
@ -216,7 +225,7 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
|
||||||
useSSL: node.isSSL,
|
useSSL: node.isSSL,
|
||||||
isLightWallet: false,
|
isLightWallet: false,
|
||||||
// FIXME: hardcoded value
|
// FIXME: hardcoded value
|
||||||
socksProxyAddress: node.socksProxyAddress);
|
socksProxyAddress: socksProxy);
|
||||||
|
|
||||||
await monero_wallet.setTrustedDaemon(node.trusted);
|
await monero_wallet.setTrustedDaemon(node.trusted);
|
||||||
syncStatus = ConnectedSyncStatus();
|
syncStatus = ConnectedSyncStatus();
|
||||||
|
|
|
@ -31,8 +31,6 @@ class PendingMoneroTransaction with PendingTransaction {
|
||||||
@override
|
@override
|
||||||
String get hex => pendingTransactionDescription.hex;
|
String get hex => pendingTransactionDescription.hex;
|
||||||
|
|
||||||
String get txKey => pendingTransactionDescription.txKey;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get amountFormatted => AmountConverter.amountIntToString(
|
String get amountFormatted => AmountConverter.amountIntToString(
|
||||||
CryptoCurrency.xmr, pendingTransactionDescription.amount);
|
CryptoCurrency.xmr, pendingTransactionDescription.amount);
|
||||||
|
|
|
@ -66,11 +66,11 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
path: "."
|
path: "."
|
||||||
ref: cake-update-v2
|
ref: cake-update-v4
|
||||||
resolved-ref: "59fdf29d72068e0522a96a8953ed7272833a9f57"
|
resolved-ref: "437dadd0bd9bf73ec6a551299577799341f6486a"
|
||||||
url: "https://github.com/cake-tech/blockchain_utils"
|
url: "https://github.com/cake-tech/blockchain_utils"
|
||||||
source: git
|
source: git
|
||||||
version: "3.3.0"
|
version: "4.3.0"
|
||||||
bluez:
|
bluez:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -599,10 +599,10 @@ packages:
|
||||||
description:
|
description:
|
||||||
path: "."
|
path: "."
|
||||||
ref: cake-update-v2
|
ref: cake-update-v2
|
||||||
resolved-ref: "93440dc5126369b873ca1fccc13c3c1240b1c5c2"
|
resolved-ref: "084fb7bf13ec42d74f26ac08c883ce07c10fca7e"
|
||||||
url: "https://github.com/cake-tech/on_chain.git"
|
url: "https://github.com/cake-tech/on_chain.git"
|
||||||
source: git
|
source: git
|
||||||
version: "3.7.0"
|
version: "6.2.0"
|
||||||
package_config:
|
package_config:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -779,11 +779,21 @@ packages:
|
||||||
socks5_proxy:
|
socks5_proxy:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: socks5_proxy
|
path: "."
|
||||||
sha256: "616818a0ea1064a4823b53c9f7eaf8da64ed82dcd51ed71371c7e54751ed5053"
|
ref: "27ad7c2efae8d7460325c74b90f660085cbd0685"
|
||||||
url: "https://pub.dev"
|
resolved-ref: "27ad7c2efae8d7460325c74b90f660085cbd0685"
|
||||||
source: hosted
|
url: "https://github.com/LacticWhale/socks_dart"
|
||||||
version: "1.0.6"
|
source: git
|
||||||
|
version: "2.1.0"
|
||||||
|
socks_socket:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
path: "."
|
||||||
|
ref: e6232c53c1595469931ababa878759a067c02e94
|
||||||
|
resolved-ref: e6232c53c1595469931ababa878759a067c02e94
|
||||||
|
url: "https://github.com/sneurlax/socks_socket"
|
||||||
|
source: git
|
||||||
|
version: "1.1.1"
|
||||||
source_gen:
|
source_gen:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -860,10 +870,19 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: timing
|
name: timing
|
||||||
sha256: "62ee18aca144e4a9f29d212f5a4c6a053be252b895ab14b5821996cff4ed90fe"
|
sha256: "70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.2"
|
version: "1.0.1"
|
||||||
|
tor_binary:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
path: "."
|
||||||
|
ref: cb811c610871a9517d47134b87c2f590c15c96c5
|
||||||
|
resolved-ref: cb811c610871a9517d47134b87c2f590c15c96c5
|
||||||
|
url: "https://github.com/MrCyjaneK/flutter-tor_binary"
|
||||||
|
source: git
|
||||||
|
version: "4.7.14"
|
||||||
tuple:
|
tuple:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
@ -3,11 +3,11 @@ import 'dart:convert';
|
||||||
|
|
||||||
import 'package:cw_core/nano_account_info_response.dart';
|
import 'package:cw_core/nano_account_info_response.dart';
|
||||||
import 'package:cw_core/utils/print_verbose.dart';
|
import 'package:cw_core/utils/print_verbose.dart';
|
||||||
|
import 'package:cw_core/utils/proxy_wrapper.dart';
|
||||||
import 'package:cw_nano/nano_block_info_response.dart';
|
import 'package:cw_nano/nano_block_info_response.dart';
|
||||||
import 'package:cw_core/n2_node.dart';
|
import 'package:cw_core/n2_node.dart';
|
||||||
import 'package:cw_nano/nano_balance.dart';
|
import 'package:cw_nano/nano_balance.dart';
|
||||||
import 'package:cw_nano/nano_transaction_model.dart';
|
import 'package:cw_nano/nano_transaction_model.dart';
|
||||||
import 'package:http/http.dart' as http;
|
|
||||||
import 'package:cw_core/node.dart';
|
import 'package:cw_core/node.dart';
|
||||||
import 'package:nanoutil/nanoutil.dart';
|
import 'package:nanoutil/nanoutil.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
@ -66,8 +66,8 @@ class NanoClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<NanoBalance> getBalance(String address) async {
|
Future<NanoBalance> getBalance(String address) async {
|
||||||
final response = await http.post(
|
final response = await ProxyWrapper().post(
|
||||||
_node!.uri,
|
clearnetUri: _node!.uri,
|
||||||
headers: getHeaders(_node!.uri.host),
|
headers: getHeaders(_node!.uri.host),
|
||||||
body: jsonEncode(
|
body: jsonEncode(
|
||||||
{
|
{
|
||||||
|
@ -76,7 +76,8 @@ class NanoClient {
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
final data = await jsonDecode(response.body);
|
|
||||||
|
final data = jsonDecode(response.body) as Map<String, dynamic>;
|
||||||
if (response.statusCode != 200 ||
|
if (response.statusCode != 200 ||
|
||||||
data["error"] != null ||
|
data["error"] != null ||
|
||||||
data["balance"] == null ||
|
data["balance"] == null ||
|
||||||
|
@ -93,8 +94,8 @@ class NanoClient {
|
||||||
|
|
||||||
Future<AccountInfoResponse?> getAccountInfo(String address) async {
|
Future<AccountInfoResponse?> getAccountInfo(String address) async {
|
||||||
try {
|
try {
|
||||||
final response = await http.post(
|
final response = await ProxyWrapper().post(
|
||||||
_node!.uri,
|
clearnetUri: _node!.uri,
|
||||||
headers: getHeaders(_node!.uri.host),
|
headers: getHeaders(_node!.uri.host),
|
||||||
body: jsonEncode(
|
body: jsonEncode(
|
||||||
{
|
{
|
||||||
|
@ -104,8 +105,9 @@ class NanoClient {
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
final data = await jsonDecode(response.body);
|
|
||||||
return AccountInfoResponse.fromJson(data as Map<String, dynamic>);
|
final data = jsonDecode(response.body) as Map<String, dynamic>;
|
||||||
|
return AccountInfoResponse.fromJson(data);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
printV("error while getting account info $e");
|
printV("error while getting account info $e");
|
||||||
return null;
|
return null;
|
||||||
|
@ -114,8 +116,8 @@ class NanoClient {
|
||||||
|
|
||||||
Future<BlockContentsResponse?> getBlockContents(String block) async {
|
Future<BlockContentsResponse?> getBlockContents(String block) async {
|
||||||
try {
|
try {
|
||||||
final response = await http.post(
|
final response = await ProxyWrapper().post(
|
||||||
_node!.uri,
|
clearnetUri: _node!.uri,
|
||||||
headers: getHeaders(_node!.uri.host),
|
headers: getHeaders(_node!.uri.host),
|
||||||
body: jsonEncode(
|
body: jsonEncode(
|
||||||
{
|
{
|
||||||
|
@ -125,7 +127,8 @@ class NanoClient {
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
final data = await jsonDecode(response.body);
|
|
||||||
|
final data = jsonDecode(response.body) as Map<String, dynamic>;
|
||||||
return BlockContentsResponse.fromJson(data["contents"] as Map<String, dynamic>);
|
return BlockContentsResponse.fromJson(data["contents"] as Map<String, dynamic>);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
printV("error while getting block info $e");
|
printV("error while getting block info $e");
|
||||||
|
@ -181,8 +184,8 @@ class NanoClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<String> requestWork(String hash) async {
|
Future<String> requestWork(String hash) async {
|
||||||
final response = await http.post(
|
final response = await ProxyWrapper().post(
|
||||||
_powNode!.uri,
|
clearnetUri: _powNode!.uri,
|
||||||
headers: getHeaders(_powNode!.uri.host),
|
headers: getHeaders(_powNode!.uri.host),
|
||||||
body: json.encode(
|
body: json.encode(
|
||||||
{
|
{
|
||||||
|
@ -191,8 +194,9 @@ class NanoClient {
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
final Map<String, dynamic> decoded = json.decode(response.body) as Map<String, dynamic>;
|
final decoded = jsonDecode(response.body) as Map<String, dynamic>;
|
||||||
if (decoded.containsKey("error")) {
|
if (decoded.containsKey("error")) {
|
||||||
throw Exception("Received error ${decoded["error"]}");
|
throw Exception("Received error ${decoded["error"]}");
|
||||||
}
|
}
|
||||||
|
@ -224,13 +228,13 @@ class NanoClient {
|
||||||
"block": block,
|
"block": block,
|
||||||
});
|
});
|
||||||
|
|
||||||
final processResponse = await http.post(
|
final processResponse = await ProxyWrapper().post(
|
||||||
_node!.uri,
|
clearnetUri: _node!.uri,
|
||||||
headers: getHeaders(_node!.uri.host),
|
headers: getHeaders(_node!.uri.host),
|
||||||
body: processBody,
|
body: processBody,
|
||||||
);
|
);
|
||||||
|
|
||||||
final Map<String, dynamic> decoded = json.decode(processResponse.body) as Map<String, dynamic>;
|
final Map<String, dynamic> decoded = jsonDecode(processResponse.body) as Map<String, dynamic>;
|
||||||
if (decoded.containsKey("error")) {
|
if (decoded.containsKey("error")) {
|
||||||
throw Exception("Received error ${decoded["error"]}");
|
throw Exception("Received error ${decoded["error"]}");
|
||||||
}
|
}
|
||||||
|
@ -423,12 +427,11 @@ class NanoClient {
|
||||||
"subtype": "receive",
|
"subtype": "receive",
|
||||||
"block": receiveBlock,
|
"block": receiveBlock,
|
||||||
});
|
});
|
||||||
final processResponse = await http.post(
|
final processResponse = await ProxyWrapper().post(
|
||||||
_node!.uri,
|
clearnetUri: _node!.uri,
|
||||||
headers: getHeaders(_node!.uri.host),
|
headers: getHeaders(_node!.uri.host),
|
||||||
body: processBody,
|
body: processBody,
|
||||||
);
|
);
|
||||||
|
|
||||||
final Map<String, dynamic> decoded = json.decode(processResponse.body) as Map<String, dynamic>;
|
final Map<String, dynamic> decoded = json.decode(processResponse.body) as Map<String, dynamic>;
|
||||||
if (decoded.containsKey("error")) {
|
if (decoded.containsKey("error")) {
|
||||||
throw Exception("Received error ${decoded["error"]}");
|
throw Exception("Received error ${decoded["error"]}");
|
||||||
|
@ -440,16 +443,17 @@ class NanoClient {
|
||||||
required String destinationAddress,
|
required String destinationAddress,
|
||||||
required String privateKey,
|
required String privateKey,
|
||||||
}) async {
|
}) async {
|
||||||
final receivableResponse = await http.post(_node!.uri,
|
final receivableResponse = await ProxyWrapper().post(
|
||||||
headers: getHeaders(_node!.uri.host),
|
clearnetUri: _node!.uri,
|
||||||
body: jsonEncode({
|
headers: getHeaders(_node!.uri.host),
|
||||||
"action": "receivable",
|
body: jsonEncode({
|
||||||
"account": destinationAddress,
|
"action": "receivable",
|
||||||
"count": "-1",
|
"account": destinationAddress,
|
||||||
"source": true,
|
"count": "-1",
|
||||||
}));
|
"source": true,
|
||||||
|
}),
|
||||||
final receivableData = await jsonDecode(receivableResponse.body);
|
);
|
||||||
|
final receivableData = jsonDecode(receivableResponse.body) as Map<String, dynamic>;
|
||||||
if (receivableData["blocks"] == "" || receivableData["blocks"] == null) {
|
if (receivableData["blocks"] == "" || receivableData["blocks"] == null) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -492,15 +496,18 @@ class NanoClient {
|
||||||
|
|
||||||
Future<List<NanoTransactionModel>> fetchTransactions(String address) async {
|
Future<List<NanoTransactionModel>> fetchTransactions(String address) async {
|
||||||
try {
|
try {
|
||||||
final response = await http.post(_node!.uri,
|
final response = await ProxyWrapper().post(
|
||||||
headers: getHeaders(_node!.uri.host),
|
clearnetUri: _node!.uri,
|
||||||
body: jsonEncode({
|
headers: getHeaders(_node!.uri.host),
|
||||||
"action": "account_history",
|
body: jsonEncode({
|
||||||
"account": address,
|
"action": "account_history",
|
||||||
"count": "100",
|
"account": address,
|
||||||
// "raw": true,
|
"count": "100",
|
||||||
}));
|
// "raw": true,
|
||||||
final data = await jsonDecode(response.body);
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
final data = jsonDecode(response.body) as Map<String, dynamic>;
|
||||||
final transactions = data["history"] is List ? data["history"] as List<dynamic> : [];
|
final transactions = data["history"] is List ? data["history"] as List<dynamic> : [];
|
||||||
|
|
||||||
// Map the transactions list to NanoTransactionModel using the factory
|
// Map the transactions list to NanoTransactionModel using the factory
|
||||||
|
@ -516,13 +523,14 @@ class NanoClient {
|
||||||
|
|
||||||
Future<List<N2Node>> getN2Reps() async {
|
Future<List<N2Node>> getN2Reps() async {
|
||||||
final uri = Uri.parse(N2_REPS_ENDPOINT);
|
final uri = Uri.parse(N2_REPS_ENDPOINT);
|
||||||
final response = await http.post(
|
final response = await ProxyWrapper().post(
|
||||||
uri,
|
clearnetUri: uri,
|
||||||
headers: getHeaders(uri.host),
|
headers: getHeaders(uri.host),
|
||||||
body: jsonEncode({"action": "reps"}),
|
body: jsonEncode({"action": "reps"}),
|
||||||
);
|
);
|
||||||
try {
|
try {
|
||||||
final List<N2Node> nodes = (json.decode(response.body) as List<dynamic>)
|
|
||||||
|
final List<N2Node> nodes = (jsonDecode(response.body) as List<dynamic>)
|
||||||
.map((dynamic e) => N2Node.fromJson(e as Map<String, dynamic>))
|
.map((dynamic e) => N2Node.fromJson(e as Map<String, dynamic>))
|
||||||
.toList();
|
.toList();
|
||||||
return nodes;
|
return nodes;
|
||||||
|
@ -533,8 +541,8 @@ class NanoClient {
|
||||||
|
|
||||||
Future<int> getRepScore(String rep) async {
|
Future<int> getRepScore(String rep) async {
|
||||||
final uri = Uri.parse(N2_REPS_ENDPOINT);
|
final uri = Uri.parse(N2_REPS_ENDPOINT);
|
||||||
final response = await http.post(
|
final response = await ProxyWrapper().post(
|
||||||
uri,
|
clearnetUri: uri,
|
||||||
headers: getHeaders(uri.host),
|
headers: getHeaders(uri.host),
|
||||||
body: jsonEncode({
|
body: jsonEncode({
|
||||||
"action": "rep_info",
|
"action": "rep_info",
|
||||||
|
@ -542,7 +550,8 @@ class NanoClient {
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
try {
|
try {
|
||||||
final N2Node node = N2Node.fromJson(json.decode(response.body) as Map<String, dynamic>);
|
|
||||||
|
final N2Node node = N2Node.fromJson(jsonDecode(response.body) as Map<String, dynamic>);
|
||||||
return node.score ?? 100;
|
return node.score ?? 100;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return 100;
|
return 100;
|
||||||
|
|
|
@ -784,11 +784,21 @@ packages:
|
||||||
socks5_proxy:
|
socks5_proxy:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: socks5_proxy
|
path: "."
|
||||||
sha256: "616818a0ea1064a4823b53c9f7eaf8da64ed82dcd51ed71371c7e54751ed5053"
|
ref: "27ad7c2efae8d7460325c74b90f660085cbd0685"
|
||||||
url: "https://pub.dev"
|
resolved-ref: "27ad7c2efae8d7460325c74b90f660085cbd0685"
|
||||||
source: hosted
|
url: "https://github.com/LacticWhale/socks_dart"
|
||||||
version: "1.0.6"
|
source: git
|
||||||
|
version: "2.1.0"
|
||||||
|
socks_socket:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
path: "."
|
||||||
|
ref: e6232c53c1595469931ababa878759a067c02e94
|
||||||
|
resolved-ref: e6232c53c1595469931ababa878759a067c02e94
|
||||||
|
url: "https://github.com/sneurlax/socks_socket"
|
||||||
|
source: git
|
||||||
|
version: "1.1.1"
|
||||||
source_gen:
|
source_gen:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -865,10 +875,19 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: timing
|
name: timing
|
||||||
sha256: "62ee18aca144e4a9f29d212f5a4c6a053be252b895ab14b5821996cff4ed90fe"
|
sha256: "70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.2"
|
version: "1.0.1"
|
||||||
|
tor_binary:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
path: "."
|
||||||
|
ref: cb811c610871a9517d47134b87c2f590c15c96c5
|
||||||
|
resolved-ref: cb811c610871a9517d47134b87c2f590c15c96c5
|
||||||
|
url: "https://github.com/MrCyjaneK/flutter-tor_binary"
|
||||||
|
source: git
|
||||||
|
version: "4.7.14"
|
||||||
tuple:
|
tuple:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
@ -40,7 +40,7 @@ class PolygonClient extends EVMChainClient {
|
||||||
Future<List<EVMChainTransactionModel>> fetchTransactions(String address,
|
Future<List<EVMChainTransactionModel>> fetchTransactions(String address,
|
||||||
{String? contractAddress}) async {
|
{String? contractAddress}) async {
|
||||||
try {
|
try {
|
||||||
final response = await httpClient.get(Uri.https("api.etherscan.io", "/v2/api", {
|
final response = await client.get(Uri.https("api.etherscan.io", "/v2/api", {
|
||||||
"chainid": "$chainId",
|
"chainid": "$chainId",
|
||||||
"module": "account",
|
"module": "account",
|
||||||
"action": contractAddress != null ? "tokentx" : "txlist",
|
"action": contractAddress != null ? "tokentx" : "txlist",
|
||||||
|
@ -68,7 +68,7 @@ class PolygonClient extends EVMChainClient {
|
||||||
@override
|
@override
|
||||||
Future<List<EVMChainTransactionModel>> fetchInternalTransactions(String address) async {
|
Future<List<EVMChainTransactionModel>> fetchInternalTransactions(String address) async {
|
||||||
try {
|
try {
|
||||||
final response = await httpClient.get(Uri.https("api.etherscan.io", "/v2/api", {
|
final response = await client.get(Uri.https("api.etherscan.io", "/v2/api", {
|
||||||
"chainid": "$chainId",
|
"chainid": "$chainId",
|
||||||
"module": "account",
|
"module": "account",
|
||||||
"action": "txlistinternal",
|
"action": "txlistinternal",
|
||||||
|
|
|
@ -2,8 +2,10 @@ import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:math' as math;
|
import 'dart:math' as math;
|
||||||
|
|
||||||
|
import 'package:blockchain_utils/blockchain_utils.dart';
|
||||||
import 'package:cw_core/crypto_currency.dart';
|
import 'package:cw_core/crypto_currency.dart';
|
||||||
import 'package:cw_core/node.dart';
|
import 'package:cw_core/node.dart';
|
||||||
|
import 'package:cw_core/utils/proxy_wrapper.dart';
|
||||||
import 'package:cw_core/solana_rpc_http_service.dart';
|
import 'package:cw_core/solana_rpc_http_service.dart';
|
||||||
import 'package:cw_core/utils/print_verbose.dart';
|
import 'package:cw_core/utils/print_verbose.dart';
|
||||||
import 'package:cw_solana/pending_solana_transaction.dart';
|
import 'package:cw_solana/pending_solana_transaction.dart';
|
||||||
|
@ -11,14 +13,17 @@ import 'package:cw_solana/solana_balance.dart';
|
||||||
import 'package:cw_solana/solana_exceptions.dart';
|
import 'package:cw_solana/solana_exceptions.dart';
|
||||||
import 'package:cw_solana/solana_transaction_model.dart';
|
import 'package:cw_solana/solana_transaction_model.dart';
|
||||||
import 'package:cw_solana/spl_token.dart';
|
import 'package:cw_solana/spl_token.dart';
|
||||||
import 'package:http/http.dart' as http;
|
|
||||||
import 'package:on_chain/solana/solana.dart';
|
import 'package:on_chain/solana/solana.dart';
|
||||||
|
import 'package:on_chain/solana/src/instructions/associated_token_account/constant.dart';
|
||||||
import 'package:on_chain/solana/src/models/pda/pda.dart';
|
import 'package:on_chain/solana/src/models/pda/pda.dart';
|
||||||
import 'package:blockchain_utils/blockchain_utils.dart';
|
import 'package:blockchain_utils/blockchain_utils.dart';
|
||||||
|
import 'package:on_chain/solana/src/rpc/models/models/confirmed_transaction_meta.dart';
|
||||||
import '.secrets.g.dart' as secrets;
|
import '.secrets.g.dart' as secrets;
|
||||||
|
|
||||||
class SolanaWalletClient {
|
class SolanaWalletClient {
|
||||||
final httpClient = http.Client();
|
// Minimum amount in SOL to consider a transaction valid (to filter spam)
|
||||||
|
static const double minValidAmount = 0.00000003;
|
||||||
|
final httpClient = ProxyWrapper().getHttpClient();
|
||||||
SolanaRPC? _provider;
|
SolanaRPC? _provider;
|
||||||
|
|
||||||
bool connect(Node node) {
|
bool connect(Node node) {
|
||||||
|
@ -82,7 +87,8 @@ class SolanaWalletClient {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<SolanaBalance?> getSplTokenBalance(String mintAddress, String walletAddress) async {
|
Future<SolanaBalance?> getSplTokenBalance(
|
||||||
|
String mintAddress, String walletAddress) async {
|
||||||
// Fetch the token accounts (a token can have multiple accounts for various uses)
|
// Fetch the token accounts (a token can have multiple accounts for various uses)
|
||||||
final tokenAccounts = await getSPLTokenAccounts(mintAddress, walletAddress);
|
final tokenAccounts = await getSPLTokenAccounts(mintAddress, walletAddress);
|
||||||
|
|
||||||
|
@ -118,14 +124,16 @@ class SolanaWalletClient {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
final fee = (feeForMessage?.toDouble() ?? 0.0) / SolanaUtils.lamportsPerSol;
|
final fee =
|
||||||
|
(feeForMessage?.toDouble() ?? 0.0) / SolanaUtils.lamportsPerSol;
|
||||||
return fee;
|
return fee;
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
return 0.0;
|
return 0.0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<double> getEstimatedFee(SolanaPublicKey publicKey, Commitment commitment) async {
|
Future<double> getEstimatedFee(
|
||||||
|
SolanaPublicKey publicKey, Commitment commitment) async {
|
||||||
final message = await _getMessageForNativeTransaction(
|
final message = await _getMessageForNativeTransaction(
|
||||||
publicKey: publicKey,
|
publicKey: publicKey,
|
||||||
destinationAddress: publicKey.toAddress().address,
|
destinationAddress: publicKey.toAddress().address,
|
||||||
|
@ -155,170 +163,88 @@ class SolanaWalletClient {
|
||||||
if (meta == null || transaction == null) return null;
|
if (meta == null || transaction == null) return null;
|
||||||
|
|
||||||
final int fee = meta.fee;
|
final int fee = meta.fee;
|
||||||
|
final feeInSol = fee / SolanaUtils.lamportsPerSol;
|
||||||
|
|
||||||
final message = transaction.message;
|
final message = transaction.message;
|
||||||
final instructions = message.compiledInstructions;
|
final instructions = message.compiledInstructions;
|
||||||
|
|
||||||
String sender = "";
|
|
||||||
String receiver = "";
|
|
||||||
|
|
||||||
String signature = (txResponse.transaction?.signatures.isEmpty ?? true)
|
String signature = (txResponse.transaction?.signatures.isEmpty ?? true)
|
||||||
? ""
|
? ""
|
||||||
: Base58Encoder.encode(txResponse.transaction!.signatures.first);
|
: Base58Encoder.encode(txResponse.transaction!.signatures.first);
|
||||||
|
|
||||||
|
|
||||||
for (final instruction in instructions) {
|
for (final instruction in instructions) {
|
||||||
final programId = message.accountKeys[instruction.programIdIndex];
|
final programId = message.accountKeys[instruction.programIdIndex];
|
||||||
|
|
||||||
if (programId == SystemProgramConst.programId) {
|
if (programId == SystemProgramConst.programId ||
|
||||||
|
programId == ComputeBudgetConst.programId) {
|
||||||
// For native solana transactions
|
// For native solana transactions
|
||||||
|
if (instruction.accounts.length < 2) continue;
|
||||||
|
|
||||||
if (txResponse.version == TransactionType.legacy) {
|
// Get the fee payer index based on transaction type
|
||||||
// For legacy transfers, the fee payer (index 0) is the sender.
|
// For legacy transfers, the first account is usually the fee payer
|
||||||
sender = message.accountKeys[0].address;
|
// For versioned, the first account in instruction is usually the fee payer
|
||||||
|
final feePayerIndex =
|
||||||
|
txResponse.version == TransactionType.legacy ? 0 : instruction.accounts[0];
|
||||||
|
|
||||||
final senderPreBalance = meta.preBalances[0];
|
final transactionModel = await _parseNativeTransaction(
|
||||||
final senderPostBalance = meta.postBalances[0];
|
message: message,
|
||||||
final feeForTx = fee / SolanaUtils.lamportsPerSol;
|
meta: meta,
|
||||||
|
fee: fee,
|
||||||
|
feeInSol: feeInSol,
|
||||||
|
feePayerIndex: feePayerIndex,
|
||||||
|
walletAddress: walletAddress,
|
||||||
|
signature: signature,
|
||||||
|
blockTime: blockTime,
|
||||||
|
);
|
||||||
|
|
||||||
// The loss on the sender's account would include both the transfer amount and the fee.
|
if (transactionModel != null) {
|
||||||
// So we would subtract the fee to calculate the actual amount that was transferred (in lamports).
|
return transactionModel;
|
||||||
final transferLamports = (senderPreBalance - senderPostBalance) - BigInt.from(fee);
|
|
||||||
|
|
||||||
// Next, we attempt to find the receiver by comparing the balance changes.
|
|
||||||
// (The index 0 is for the sender so we skip it.)
|
|
||||||
bool foundReceiver = false;
|
|
||||||
for (int i = 1; i < meta.preBalances.length; i++) {
|
|
||||||
// The increase in balance on the receiver account should correspond to the transfer amount we calculated earlieer.
|
|
||||||
final pre = meta.preBalances[i];
|
|
||||||
final post = meta.postBalances[i];
|
|
||||||
if ((post - pre) == transferLamports) {
|
|
||||||
receiver = message.accountKeys[i].address;
|
|
||||||
foundReceiver = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!foundReceiver) {
|
|
||||||
// Optionally (and rarely), if no account shows the exact expected change,
|
|
||||||
// we set the receiver address to unknown.
|
|
||||||
receiver = "unknown";
|
|
||||||
}
|
|
||||||
|
|
||||||
final amount = transferLamports / BigInt.from(1e9);
|
|
||||||
|
|
||||||
return SolanaTransactionModel(
|
|
||||||
isOutgoingTx: sender == walletAddress,
|
|
||||||
from: sender,
|
|
||||||
to: receiver,
|
|
||||||
id: signature,
|
|
||||||
amount: amount.abs(),
|
|
||||||
programId: SystemProgramConst.programId.address,
|
|
||||||
tokenSymbol: 'SOL',
|
|
||||||
blockTimeInInt: blockTime?.toInt() ?? 0,
|
|
||||||
fee: feeForTx,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
if (instruction.accounts.length < 2) continue;
|
|
||||||
final senderIndex = instruction.accounts[0];
|
|
||||||
final receiverIndex = instruction.accounts[1];
|
|
||||||
|
|
||||||
sender = message.accountKeys[senderIndex].address;
|
|
||||||
receiver = message.accountKeys[receiverIndex].address;
|
|
||||||
|
|
||||||
final feeForTx = fee / SolanaUtils.lamportsPerSol;
|
|
||||||
|
|
||||||
final preBalances = meta.preBalances;
|
|
||||||
final postBalances = meta.postBalances;
|
|
||||||
|
|
||||||
final amountInString =
|
|
||||||
(((preBalances[senderIndex] - postBalances[senderIndex]) / BigInt.from(1e9))
|
|
||||||
.toDouble() -
|
|
||||||
feeForTx)
|
|
||||||
.toStringAsFixed(6);
|
|
||||||
|
|
||||||
final amount = double.parse(amountInString);
|
|
||||||
|
|
||||||
return SolanaTransactionModel(
|
|
||||||
isOutgoingTx: sender == walletAddress,
|
|
||||||
from: sender,
|
|
||||||
to: receiver,
|
|
||||||
id: signature,
|
|
||||||
amount: amount.abs(),
|
|
||||||
programId: SystemProgramConst.programId.address,
|
|
||||||
tokenSymbol: 'SOL',
|
|
||||||
blockTimeInInt: blockTime?.toInt() ?? 0,
|
|
||||||
fee: feeForTx,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
} else if (programId == SPLTokenProgramConst.tokenProgramId) {
|
} else if (programId == SPLTokenProgramConst.tokenProgramId) {
|
||||||
// For SPL Token transactions
|
// For SPL Token transactions
|
||||||
if (instruction.accounts.length < 2) continue;
|
if (instruction.accounts.length < 2) continue;
|
||||||
|
|
||||||
final preBalances = meta.preTokenBalances;
|
final transactionModel = await _parseSPLTokenTransaction(
|
||||||
final postBalances = meta.postTokenBalances;
|
message: message,
|
||||||
|
meta: meta,
|
||||||
double amount = 0.0;
|
fee: fee,
|
||||||
bool isOutgoing = false;
|
feeInSol: feeInSol,
|
||||||
String? mintAddress;
|
instruction: instruction,
|
||||||
|
walletAddress: walletAddress,
|
||||||
double userPreAmount = 0.0;
|
signature: signature,
|
||||||
if (preBalances != null && preBalances.isNotEmpty) {
|
blockTime: blockTime,
|
||||||
for (final preBal in preBalances) {
|
splTokenSymbol: splTokenSymbol,
|
||||||
if (preBal.owner?.address == walletAddress) {
|
|
||||||
userPreAmount = preBal.uiTokenAmount.uiAmount ?? 0.0;
|
|
||||||
|
|
||||||
mintAddress = preBal.mint.address;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
double userPostAmount = 0.0;
|
|
||||||
if (postBalances != null && postBalances.isNotEmpty) {
|
|
||||||
for (final postBal in postBalances) {
|
|
||||||
if (postBal.owner?.address == walletAddress) {
|
|
||||||
userPostAmount = postBal.uiTokenAmount.uiAmount ?? 0.0;
|
|
||||||
|
|
||||||
mintAddress ??= postBal.mint.address;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final diff = userPreAmount - userPostAmount;
|
|
||||||
final rawAmount = diff.abs();
|
|
||||||
|
|
||||||
final amountInString = rawAmount.toStringAsFixed(6);
|
|
||||||
amount = double.parse(amountInString);
|
|
||||||
|
|
||||||
isOutgoing = diff > 0;
|
|
||||||
|
|
||||||
if (mintAddress == null && instruction.accounts.length >= 4) {
|
|
||||||
final mintIndex = instruction.accounts[3];
|
|
||||||
mintAddress = message.accountKeys[mintIndex].address;
|
|
||||||
}
|
|
||||||
|
|
||||||
final sender = message.accountKeys[instruction.accounts[0]].address;
|
|
||||||
final receiver = message.accountKeys[instruction.accounts[1]].address;
|
|
||||||
|
|
||||||
String? tokenSymbol = splTokenSymbol;
|
|
||||||
|
|
||||||
if (tokenSymbol == null && mintAddress != null) {
|
|
||||||
final token = await getTokenInfo(mintAddress);
|
|
||||||
tokenSymbol = token?.symbol;
|
|
||||||
}
|
|
||||||
|
|
||||||
return SolanaTransactionModel(
|
|
||||||
isOutgoingTx: isOutgoing,
|
|
||||||
from: sender,
|
|
||||||
to: receiver,
|
|
||||||
id: signature,
|
|
||||||
amount: amount,
|
|
||||||
programId: SPLTokenProgramConst.tokenProgramId.address,
|
|
||||||
blockTimeInInt: blockTime?.toInt() ?? 0,
|
|
||||||
tokenSymbol: tokenSymbol ?? '',
|
|
||||||
fee: fee / SolanaUtils.lamportsPerSol,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (transactionModel != null) {
|
||||||
|
return transactionModel;
|
||||||
|
}
|
||||||
|
} else if (programId == AssociatedTokenAccountProgramConst.associatedTokenProgramId) {
|
||||||
|
// For ATA program, we need to check if this is a create account transaction
|
||||||
|
// or if it's part of a normal token transfer
|
||||||
|
|
||||||
|
// We skip this transaction if this is the only instruction (this means that it's a create account transaction)
|
||||||
|
if (instructions.length == 1) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We look for a token transfer instruction in the same transaction
|
||||||
|
bool hasTokenTransfer = false;
|
||||||
|
for (final otherInstruction in instructions) {
|
||||||
|
final otherProgramId = message.accountKeys[otherInstruction.programIdIndex];
|
||||||
|
if (otherProgramId == SPLTokenProgramConst.tokenProgramId) {
|
||||||
|
hasTokenTransfer = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there's no token transfer instruction, it means this is just an ATA creation transaction
|
||||||
|
if (!hasTokenTransfer) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -330,6 +256,144 @@ class SolanaWalletClient {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<SolanaTransactionModel?> _parseNativeTransaction({
|
||||||
|
required VersionedMessage message,
|
||||||
|
required ConfirmedTransactionMeta meta,
|
||||||
|
required int fee,
|
||||||
|
required double feeInSol,
|
||||||
|
required int feePayerIndex,
|
||||||
|
required String walletAddress,
|
||||||
|
required String signature,
|
||||||
|
required BigInt? blockTime,
|
||||||
|
}) async {
|
||||||
|
// Calculate total balance changes across all accounts
|
||||||
|
BigInt totalBalanceChange = BigInt.zero;
|
||||||
|
String? sender;
|
||||||
|
String? receiver;
|
||||||
|
|
||||||
|
for (int i = 0; i < meta.preBalances.length; i++) {
|
||||||
|
final preBalance = meta.preBalances[i];
|
||||||
|
final postBalance = meta.postBalances[i];
|
||||||
|
final balanceChange = preBalance - postBalance;
|
||||||
|
|
||||||
|
if (balanceChange > BigInt.zero) {
|
||||||
|
// This account sent funds
|
||||||
|
sender = message.accountKeys[i].address;
|
||||||
|
totalBalanceChange += balanceChange;
|
||||||
|
} else if (balanceChange < BigInt.zero) {
|
||||||
|
// This account received funds
|
||||||
|
receiver = message.accountKeys[i].address;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We subtract the fee from total balance change if the fee payer is the sender
|
||||||
|
if (sender == message.accountKeys[feePayerIndex].address) {
|
||||||
|
totalBalanceChange -= BigInt.from(fee);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sender == null || receiver == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
final amount = totalBalanceChange / BigInt.from(1e9);
|
||||||
|
final amountInSol = amount.abs().toDouble();
|
||||||
|
|
||||||
|
// Skip transactions with very small amounts (likely spam)
|
||||||
|
if (amountInSol < minValidAmount) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SolanaTransactionModel(
|
||||||
|
isOutgoingTx: sender == walletAddress,
|
||||||
|
from: sender,
|
||||||
|
to: receiver,
|
||||||
|
id: signature,
|
||||||
|
amount: amountInSol,
|
||||||
|
programId: SystemProgramConst.programId.address,
|
||||||
|
tokenSymbol: 'SOL',
|
||||||
|
blockTimeInInt: blockTime?.toInt() ?? 0,
|
||||||
|
fee: feeInSol,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<SolanaTransactionModel?> _parseSPLTokenTransaction({
|
||||||
|
required VersionedMessage message,
|
||||||
|
required ConfirmedTransactionMeta meta,
|
||||||
|
required int fee,
|
||||||
|
required double feeInSol,
|
||||||
|
required CompiledInstruction instruction,
|
||||||
|
required String walletAddress,
|
||||||
|
required String signature,
|
||||||
|
required BigInt? blockTime,
|
||||||
|
String? splTokenSymbol,
|
||||||
|
}) async {
|
||||||
|
final preBalances = meta.preTokenBalances;
|
||||||
|
final postBalances = meta.postTokenBalances;
|
||||||
|
|
||||||
|
double amount = 0.0;
|
||||||
|
bool isOutgoing = false;
|
||||||
|
String? mintAddress;
|
||||||
|
|
||||||
|
double userPreAmount = 0.0;
|
||||||
|
if (preBalances != null && preBalances.isNotEmpty) {
|
||||||
|
for (final preBal in preBalances) {
|
||||||
|
if (preBal.owner?.address == walletAddress) {
|
||||||
|
userPreAmount = preBal.uiTokenAmount.uiAmount ?? 0.0;
|
||||||
|
|
||||||
|
mintAddress = preBal.mint.address;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
double userPostAmount = 0.0;
|
||||||
|
if (postBalances != null && postBalances.isNotEmpty) {
|
||||||
|
for (final postBal in postBalances) {
|
||||||
|
if (postBal.owner?.address == walletAddress) {
|
||||||
|
userPostAmount = postBal.uiTokenAmount.uiAmount ?? 0.0;
|
||||||
|
|
||||||
|
mintAddress ??= postBal.mint.address;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final diff = userPreAmount - userPostAmount;
|
||||||
|
final rawAmount = diff.abs();
|
||||||
|
|
||||||
|
final amountInString = rawAmount.toStringAsFixed(6);
|
||||||
|
amount = double.parse(amountInString);
|
||||||
|
|
||||||
|
isOutgoing = diff > 0;
|
||||||
|
|
||||||
|
if (mintAddress == null && instruction.accounts.length >= 4) {
|
||||||
|
final mintIndex = instruction.accounts[3];
|
||||||
|
mintAddress = message.accountKeys[mintIndex].address;
|
||||||
|
}
|
||||||
|
|
||||||
|
final sender = message.accountKeys[instruction.accounts[0]].address;
|
||||||
|
final receiver = message.accountKeys[instruction.accounts[1]].address;
|
||||||
|
|
||||||
|
String? tokenSymbol = splTokenSymbol;
|
||||||
|
|
||||||
|
if (tokenSymbol == null && mintAddress != null) {
|
||||||
|
final token = await getTokenInfo(mintAddress);
|
||||||
|
tokenSymbol = token?.symbol;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SolanaTransactionModel(
|
||||||
|
isOutgoingTx: isOutgoing,
|
||||||
|
from: sender,
|
||||||
|
to: receiver,
|
||||||
|
id: signature,
|
||||||
|
amount: amount,
|
||||||
|
programId: SPLTokenProgramConst.tokenProgramId.address,
|
||||||
|
blockTimeInInt: blockTime?.toInt() ?? 0,
|
||||||
|
tokenSymbol: tokenSymbol ?? '',
|
||||||
|
fee: feeInSol,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/// Load the Address's transactions into the account
|
/// Load the Address's transactions into the account
|
||||||
Future<List<SolanaTransactionModel>> fetchTransactions(
|
Future<List<SolanaTransactionModel>> fetchTransactions(
|
||||||
SolAddress address, {
|
SolAddress address, {
|
||||||
|
@ -369,23 +433,28 @@ class SolanaWalletClient {
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
final versionedBatchResponses = batchResponses.whereType<VersionedTransactionResponse>();
|
final versionedBatchResponses =
|
||||||
|
batchResponses.whereType<VersionedTransactionResponse>();
|
||||||
|
|
||||||
final parsedTransactionsFutures = versionedBatchResponses.map((tx) => parseTransaction(
|
final parsedTransactionsFutures =
|
||||||
txResponse: tx,
|
versionedBatchResponses.map((tx) => parseTransaction(
|
||||||
splTokenSymbol: splTokenSymbol,
|
txResponse: tx,
|
||||||
walletAddress: walletAddress?.address ?? address.address,
|
splTokenSymbol: splTokenSymbol,
|
||||||
));
|
walletAddress: walletAddress?.address ?? address.address,
|
||||||
|
));
|
||||||
|
|
||||||
final parsedTransactions = await Future.wait(parsedTransactionsFutures);
|
final parsedTransactions = await Future.wait(parsedTransactionsFutures);
|
||||||
|
|
||||||
transactions.addAll(parsedTransactions.whereType<SolanaTransactionModel>().toList());
|
transactions.addAll(
|
||||||
|
parsedTransactions.whereType<SolanaTransactionModel>().toList());
|
||||||
|
|
||||||
// Calling the callback after each batch is processed, therefore passing the current list of transactions.
|
// Only update UI if we have new valid transactions
|
||||||
onUpdate(List<SolanaTransactionModel>.from(transactions));
|
if (parsedTransactions.isNotEmpty) {
|
||||||
|
onUpdate(List<SolanaTransactionModel>.from(transactions));
|
||||||
|
}
|
||||||
|
|
||||||
if (i + batchSize < signatures.length) {
|
if (i + batchSize < signatures.length) {
|
||||||
await Future.delayed(const Duration(milliseconds: 500));
|
await Future.delayed(const Duration(milliseconds: 300));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -446,8 +515,8 @@ class SolanaWalletClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<SPLToken?> fetchSPLTokenInfo(String mintAddress) async {
|
Future<SPLToken?> fetchSPLTokenInfo(String mintAddress) async {
|
||||||
final programAddress =
|
final programAddress = MetaplexTokenMetaDataProgramUtils.findMetadataPda(
|
||||||
MetaplexTokenMetaDataProgramUtils.findMetadataPda(mint: SolAddress(mintAddress));
|
mint: SolAddress(mintAddress));
|
||||||
|
|
||||||
final token = await _provider!.request(
|
final token = await _provider!.request(
|
||||||
SolanaRPCGetMetadataAccount(
|
SolanaRPCGetMetadataAccount(
|
||||||
|
@ -468,8 +537,9 @@ class SolanaWalletClient {
|
||||||
// iconPath = await _client.getIconImageFromTokenUri(metadata.uri);
|
// iconPath = await _client.getIconImageFromTokenUri(metadata.uri);
|
||||||
// } catch (_) {}
|
// } catch (_) {}
|
||||||
|
|
||||||
String filteredTokenSymbol =
|
String filteredTokenSymbol = metadata.symbol
|
||||||
metadata.symbol.replaceFirst(RegExp('^\\\$'), '').replaceAll('\u0000', '');
|
.replaceFirst(RegExp('^\\\$'), '')
|
||||||
|
.replaceAll('\u0000', '');
|
||||||
|
|
||||||
return SPLToken.fromMetadata(
|
return SPLToken.fromMetadata(
|
||||||
name: metadata.name,
|
name: metadata.name,
|
||||||
|
@ -585,7 +655,8 @@ class SolanaWalletClient {
|
||||||
return message;
|
return message;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<double> _getFeeFromCompiledMessage(Message message, Commitment commitment) async {
|
Future<double> _getFeeFromCompiledMessage(
|
||||||
|
Message message, Commitment commitment) async {
|
||||||
final base64Message = base64Encode(message.serialize());
|
final base64Message = base64Encode(message.serialize());
|
||||||
|
|
||||||
final fee = await getFeeForMessage(base64Message, commitment);
|
final fee = await getFeeForMessage(base64Message, commitment);
|
||||||
|
@ -724,7 +795,8 @@ class SolanaWalletClient {
|
||||||
required SolAddress mintAddress,
|
required SolAddress mintAddress,
|
||||||
required bool shouldCreateATA,
|
required bool shouldCreateATA,
|
||||||
}) async {
|
}) async {
|
||||||
final associatedTokenAccount = AssociatedTokenAccountProgramUtils.associatedTokenAccount(
|
final associatedTokenAccount =
|
||||||
|
AssociatedTokenAccountProgramUtils.associatedTokenAccount(
|
||||||
mint: mintAddress,
|
mint: mintAddress,
|
||||||
owner: ownerAddress,
|
owner: ownerAddress,
|
||||||
);
|
);
|
||||||
|
@ -732,19 +804,24 @@ class SolanaWalletClient {
|
||||||
SolanaAccountInfo? accountInfo;
|
SolanaAccountInfo? accountInfo;
|
||||||
try {
|
try {
|
||||||
accountInfo = await _provider!.request(
|
accountInfo = await _provider!.request(
|
||||||
SolanaRPCGetAccountInfo(account: associatedTokenAccount.address),
|
SolanaRPCGetAccountInfo(
|
||||||
|
account: associatedTokenAccount.address,
|
||||||
|
commitment: Commitment.confirmed,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
accountInfo = null;
|
accountInfo = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If aacountInfo is null, signifies that the associatedTokenAccount has only been created locally and not been broadcasted to the blockchain.
|
// If account exists, we return the associated token account
|
||||||
if (accountInfo != null) return associatedTokenAccount;
|
if (accountInfo != null) return associatedTokenAccount;
|
||||||
|
|
||||||
if (!shouldCreateATA) return null;
|
if (!shouldCreateATA) return null;
|
||||||
|
|
||||||
|
final payerAddress = payerPrivateKey.publicKey().toAddress();
|
||||||
|
|
||||||
final createAssociatedTokenAccount = AssociatedTokenAccountProgram.associatedTokenAccount(
|
final createAssociatedTokenAccount = AssociatedTokenAccountProgram.associatedTokenAccount(
|
||||||
payer: payerPrivateKey.publicKey().toAddress(),
|
payer: payerAddress,
|
||||||
associatedToken: associatedTokenAccount.address,
|
associatedToken: associatedTokenAccount.address,
|
||||||
owner: ownerAddress,
|
owner: ownerAddress,
|
||||||
mint: mintAddress,
|
mint: mintAddress,
|
||||||
|
@ -753,19 +830,23 @@ class SolanaWalletClient {
|
||||||
final blockhash = await _getLatestBlockhash(Commitment.confirmed);
|
final blockhash = await _getLatestBlockhash(Commitment.confirmed);
|
||||||
|
|
||||||
final transaction = SolanaTransaction(
|
final transaction = SolanaTransaction(
|
||||||
payerKey: payerPrivateKey.publicKey().toAddress(),
|
payerKey: payerAddress,
|
||||||
instructions: [createAssociatedTokenAccount],
|
instructions: [createAssociatedTokenAccount],
|
||||||
recentBlockhash: blockhash,
|
recentBlockhash: blockhash,
|
||||||
|
type: TransactionType.v0,
|
||||||
);
|
);
|
||||||
|
|
||||||
transaction.sign([payerPrivateKey]);
|
final serializedTransaction = await _signTransactionInternal(
|
||||||
|
ownerPrivateKey: payerPrivateKey,
|
||||||
|
transaction: transaction,
|
||||||
|
);
|
||||||
|
|
||||||
await sendTransaction(
|
await sendTransaction(
|
||||||
serializedTransaction: transaction.serializeString(),
|
serializedTransaction: serializedTransaction,
|
||||||
commitment: Commitment.confirmed,
|
commitment: Commitment.confirmed,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Delay for propagation on the blockchain for newly created associated token addresses
|
// Wait for confirmation
|
||||||
await Future.delayed(const Duration(seconds: 2));
|
await Future.delayed(const Duration(seconds: 2));
|
||||||
|
|
||||||
return associatedTokenAccount;
|
return associatedTokenAccount;
|
||||||
|
@ -786,7 +867,8 @@ class SolanaWalletClient {
|
||||||
final amount = (inputAmount * math.pow(10, tokenDecimals)).toInt();
|
final amount = (inputAmount * math.pow(10, tokenDecimals)).toInt();
|
||||||
ProgramDerivedAddress? associatedSenderAccount;
|
ProgramDerivedAddress? associatedSenderAccount;
|
||||||
try {
|
try {
|
||||||
associatedSenderAccount = AssociatedTokenAccountProgramUtils.associatedTokenAccount(
|
associatedSenderAccount =
|
||||||
|
AssociatedTokenAccountProgramUtils.associatedTokenAccount(
|
||||||
mint: mintAddress,
|
mint: mintAddress,
|
||||||
owner: ownerPrivateKey.publicKey().toAddress(),
|
owner: ownerPrivateKey.publicKey().toAddress(),
|
||||||
);
|
);
|
||||||
|
@ -890,7 +972,7 @@ class SolanaWalletClient {
|
||||||
}) async {
|
}) async {
|
||||||
/// Sign the transaction with the owner's private key.
|
/// Sign the transaction with the owner's private key.
|
||||||
final ownerSignature = ownerPrivateKey.sign(transaction.serializeMessage());
|
final ownerSignature = ownerPrivateKey.sign(transaction.serializeMessage());
|
||||||
|
|
||||||
transaction.addSignature(ownerPrivateKey.publicKey().toAddress(), ownerSignature);
|
transaction.addSignature(ownerPrivateKey.publicKey().toAddress(), ownerSignature);
|
||||||
|
|
||||||
/// Serialize the transaction.
|
/// Serialize the transaction.
|
||||||
|
@ -921,7 +1003,8 @@ class SolanaWalletClient {
|
||||||
if (uri.isEmpty || uri == '…') return null;
|
if (uri.isEmpty || uri == '…') return null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final response = await httpClient.get(Uri.parse(uri));
|
final client = ProxyWrapper().getHttpIOClient();
|
||||||
|
final response = await client.get(Uri.parse(uri));
|
||||||
|
|
||||||
final jsonResponse = json.decode(response.body) as Map<String, dynamic>;
|
final jsonResponse = json.decode(response.body) as Map<String, dynamic>;
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ import 'dart:developer';
|
||||||
import 'package:blockchain_utils/blockchain_utils.dart';
|
import 'package:blockchain_utils/blockchain_utils.dart';
|
||||||
import 'package:cw_core/crypto_currency.dart';
|
import 'package:cw_core/crypto_currency.dart';
|
||||||
import 'package:cw_core/node.dart';
|
import 'package:cw_core/node.dart';
|
||||||
|
import 'package:cw_core/utils/proxy_wrapper.dart';
|
||||||
import 'package:cw_tron/pending_tron_transaction.dart';
|
import 'package:cw_tron/pending_tron_transaction.dart';
|
||||||
import 'package:cw_tron/tron_abi.dart';
|
import 'package:cw_tron/tron_abi.dart';
|
||||||
import 'package:cw_tron/tron_balance.dart';
|
import 'package:cw_tron/tron_balance.dart';
|
||||||
|
@ -13,12 +14,12 @@ import 'package:cw_tron/tron_token.dart';
|
||||||
import 'package:cw_tron/tron_transaction_model.dart';
|
import 'package:cw_tron/tron_transaction_model.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:http/http.dart';
|
|
||||||
import '.secrets.g.dart' as secrets;
|
import '.secrets.g.dart' as secrets;
|
||||||
import 'package:on_chain/on_chain.dart';
|
import 'package:on_chain/on_chain.dart';
|
||||||
|
|
||||||
class TronClient {
|
class TronClient {
|
||||||
final httpClient = Client();
|
late final client = ProxyWrapper().getHttpIOClient();
|
||||||
|
|
||||||
TronProvider? _provider;
|
TronProvider? _provider;
|
||||||
// This is an internal tracker, so we don't have to "refetch".
|
// This is an internal tracker, so we don't have to "refetch".
|
||||||
int _nativeTxEstimatedFee = 0;
|
int _nativeTxEstimatedFee = 0;
|
||||||
|
@ -28,7 +29,7 @@ class TronClient {
|
||||||
Future<List<TronTransactionModel>> fetchTransactions(String address,
|
Future<List<TronTransactionModel>> fetchTransactions(String address,
|
||||||
{String? contractAddress}) async {
|
{String? contractAddress}) async {
|
||||||
try {
|
try {
|
||||||
final response = await httpClient.get(
|
final response = await client.get(
|
||||||
Uri.https(
|
Uri.https(
|
||||||
"api.trongrid.io",
|
"api.trongrid.io",
|
||||||
"/v1/accounts/$address/transactions",
|
"/v1/accounts/$address/transactions",
|
||||||
|
@ -61,7 +62,7 @@ class TronClient {
|
||||||
|
|
||||||
Future<List<TronTRC20TransactionModel>> fetchTrc20ExcludedTransactions(String address) async {
|
Future<List<TronTRC20TransactionModel>> fetchTrc20ExcludedTransactions(String address) async {
|
||||||
try {
|
try {
|
||||||
final response = await httpClient.get(
|
final response = await client.get(
|
||||||
Uri.https(
|
Uri.https(
|
||||||
"api.trongrid.io",
|
"api.trongrid.io",
|
||||||
"/v1/accounts/$address/transactions/trc20",
|
"/v1/accounts/$address/transactions/trc20",
|
||||||
|
|
|
@ -1,22 +1,22 @@
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:cw_core/utils/proxy_wrapper.dart';
|
||||||
import 'package:on_chain/tron/tron.dart';
|
import 'package:on_chain/tron/tron.dart';
|
||||||
import '.secrets.g.dart' as secrets;
|
import '.secrets.g.dart' as secrets;
|
||||||
|
|
||||||
class TronHTTPProvider implements TronServiceProvider {
|
class TronHTTPProvider implements TronServiceProvider {
|
||||||
TronHTTPProvider(
|
TronHTTPProvider(
|
||||||
{required this.url,
|
{required this.url,
|
||||||
http.Client? client,
|
this.defaultRequestTimeout = const Duration(seconds: 30)});
|
||||||
this.defaultRequestTimeout = const Duration(seconds: 30)})
|
|
||||||
: client = client ?? http.Client();
|
|
||||||
@override
|
@override
|
||||||
final String url;
|
final String url;
|
||||||
final http.Client client;
|
late final client = ProxyWrapper().getHttpIOClient();
|
||||||
final Duration defaultRequestTimeout;
|
final Duration defaultRequestTimeout;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Map<String, dynamic>> get(TronRequestDetails params, [Duration? timeout]) async {
|
Future<Map<String, dynamic>> get(TronRequestDetails params,
|
||||||
|
[Duration? timeout]) async {
|
||||||
final response = await client.get(Uri.parse(params.url(url)), headers: {
|
final response = await client.get(Uri.parse(params.url(url)), headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
if (url.contains("trongrid")) 'TRON-PRO-API-KEY': secrets.tronGridApiKey,
|
if (url.contains("trongrid")) 'TRON-PRO-API-KEY': secrets.tronGridApiKey,
|
||||||
|
@ -27,13 +27,16 @@ class TronHTTPProvider implements TronServiceProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Map<String, dynamic>> post(TronRequestDetails params, [Duration? timeout]) async {
|
Future<Map<String, dynamic>> post(TronRequestDetails params,
|
||||||
|
[Duration? timeout]) async {
|
||||||
final response = await client
|
final response = await client
|
||||||
.post(Uri.parse(params.url(url)),
|
.post(Uri.parse(params.url(url)),
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
if (url.contains("trongrid")) 'TRON-PRO-API-KEY': secrets.tronGridApiKey,
|
if (url.contains("trongrid"))
|
||||||
if (url.contains("nownodes")) 'api-key': secrets.tronNowNodesApiKey,
|
'TRON-PRO-API-KEY': secrets.tronGridApiKey,
|
||||||
|
if (url.contains("nownodes"))
|
||||||
|
'api-key': secrets.tronNowNodesApiKey,
|
||||||
},
|
},
|
||||||
body: params.toRequestBody())
|
body: params.toRequestBody())
|
||||||
.timeout(timeout ?? defaultRequestTimeout);
|
.timeout(timeout ?? defaultRequestTimeout);
|
||||||
|
|
|
@ -110,7 +110,16 @@ int getUnlockedBalance({int accountIndex = 0}) =>
|
||||||
|
|
||||||
int getCurrentHeight() => wownero.Wallet_blockChainHeight(wptr!);
|
int getCurrentHeight() => wownero.Wallet_blockChainHeight(wptr!);
|
||||||
|
|
||||||
int getNodeHeightSync() => wownero.Wallet_daemonBlockChainHeight(wptr!);
|
int cachedNodeHeight = 0;
|
||||||
|
int getNodeHeightSync() {
|
||||||
|
(() async {
|
||||||
|
final wptrAddress = wptr!.address;
|
||||||
|
cachedNodeHeight = await Isolate.run(() async {
|
||||||
|
return wownero.Wallet_daemonBlockChainHeight(Pointer.fromAddress(wptrAddress));
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
return cachedNodeHeight;
|
||||||
|
}
|
||||||
|
|
||||||
bool isConnectedSync() => wownero.Wallet_connected(wptr!) != 0;
|
bool isConnectedSync() => wownero.Wallet_connected(wptr!) != 0;
|
||||||
|
|
||||||
|
@ -154,7 +163,7 @@ Future<bool> setupNodeSync(
|
||||||
}
|
}
|
||||||
|
|
||||||
void startRefreshSync() {
|
void startRefreshSync() {
|
||||||
wownero.Wallet_refreshAsync(wptr!);
|
// wownero.Wallet_refreshAsync(wptr!);
|
||||||
wownero.Wallet_startRefresh(wptr!);
|
wownero.Wallet_startRefresh(wptr!);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@ import 'package:cw_core/transaction_direction.dart';
|
||||||
import 'package:cw_core/transaction_priority.dart';
|
import 'package:cw_core/transaction_priority.dart';
|
||||||
import 'package:cw_core/unspent_coins_info.dart';
|
import 'package:cw_core/unspent_coins_info.dart';
|
||||||
import 'package:cw_core/utils/print_verbose.dart';
|
import 'package:cw_core/utils/print_verbose.dart';
|
||||||
|
import 'package:cw_core/utils/proxy_wrapper.dart';
|
||||||
import 'package:cw_core/wallet_base.dart';
|
import 'package:cw_core/wallet_base.dart';
|
||||||
import 'package:cw_core/wallet_info.dart';
|
import 'package:cw_core/wallet_info.dart';
|
||||||
import 'package:cw_core/wownero_amount_format.dart';
|
import 'package:cw_core/wownero_amount_format.dart';
|
||||||
|
@ -183,6 +184,14 @@ abstract class WowneroWalletBase
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> connectToNode({required Node node}) async {
|
Future<void> connectToNode({required Node node}) async {
|
||||||
|
String socksProxy = node.socksProxyAddress ?? '';
|
||||||
|
printV("bootstrapped: ${CakeTor.instance.bootstrapped}");
|
||||||
|
printV(" enabled: ${CakeTor.instance.enabled}");
|
||||||
|
printV(" port: ${CakeTor.instance.port}");
|
||||||
|
printV(" started: ${CakeTor.instance.started}");
|
||||||
|
if (CakeTor.instance.enabled) {
|
||||||
|
socksProxy = "127.0.0.1:${CakeTor.instance.port}";
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
syncStatus = ConnectingSyncStatus();
|
syncStatus = ConnectingSyncStatus();
|
||||||
await wownero_wallet.setupNode(
|
await wownero_wallet.setupNode(
|
||||||
|
@ -192,7 +201,7 @@ abstract class WowneroWalletBase
|
||||||
useSSL: node.isSSL,
|
useSSL: node.isSSL,
|
||||||
isLightWallet: false,
|
isLightWallet: false,
|
||||||
// FIXME: hardcoded value
|
// FIXME: hardcoded value
|
||||||
socksProxyAddress: node.socksProxyAddress);
|
socksProxyAddress: socksProxy);
|
||||||
|
|
||||||
wownero_wallet.setTrustedDaemon(node.trusted);
|
wownero_wallet.setTrustedDaemon(node.trusted);
|
||||||
syncStatus = ConnectedSyncStatus();
|
syncStatus = ConnectedSyncStatus();
|
||||||
|
|
|
@ -683,11 +683,21 @@ packages:
|
||||||
socks5_proxy:
|
socks5_proxy:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: socks5_proxy
|
path: "."
|
||||||
sha256: "616818a0ea1064a4823b53c9f7eaf8da64ed82dcd51ed71371c7e54751ed5053"
|
ref: "27ad7c2efae8d7460325c74b90f660085cbd0685"
|
||||||
url: "https://pub.dev"
|
resolved-ref: "27ad7c2efae8d7460325c74b90f660085cbd0685"
|
||||||
source: hosted
|
url: "https://github.com/LacticWhale/socks_dart"
|
||||||
version: "1.0.6"
|
source: git
|
||||||
|
version: "2.1.0"
|
||||||
|
socks_socket:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
path: "."
|
||||||
|
ref: e6232c53c1595469931ababa878759a067c02e94
|
||||||
|
resolved-ref: e6232c53c1595469931ababa878759a067c02e94
|
||||||
|
url: "https://github.com/sneurlax/socks_socket"
|
||||||
|
source: git
|
||||||
|
version: "1.1.1"
|
||||||
source_gen:
|
source_gen:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -764,10 +774,19 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: timing
|
name: timing
|
||||||
sha256: "62ee18aca144e4a9f29d212f5a4c6a053be252b895ab14b5821996cff4ed90fe"
|
sha256: "70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.2"
|
version: "1.0.1"
|
||||||
|
tor_binary:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
path: "."
|
||||||
|
ref: cb811c610871a9517d47134b87c2f590c15c96c5
|
||||||
|
resolved-ref: cb811c610871a9517d47134b87c2f590c15c96c5
|
||||||
|
url: "https://github.com/MrCyjaneK/flutter-tor_binary"
|
||||||
|
source: git
|
||||||
|
version: "4.7.14"
|
||||||
tuple:
|
tuple:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
@ -680,11 +680,21 @@ packages:
|
||||||
socks5_proxy:
|
socks5_proxy:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: socks5_proxy
|
path: "."
|
||||||
sha256: "616818a0ea1064a4823b53c9f7eaf8da64ed82dcd51ed71371c7e54751ed5053"
|
ref: "27ad7c2efae8d7460325c74b90f660085cbd0685"
|
||||||
url: "https://pub.dev"
|
resolved-ref: "27ad7c2efae8d7460325c74b90f660085cbd0685"
|
||||||
source: hosted
|
url: "https://github.com/LacticWhale/socks_dart"
|
||||||
version: "1.0.6"
|
source: git
|
||||||
|
version: "2.1.0"
|
||||||
|
socks_socket:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
path: "."
|
||||||
|
ref: e6232c53c1595469931ababa878759a067c02e94
|
||||||
|
resolved-ref: e6232c53c1595469931ababa878759a067c02e94
|
||||||
|
url: "https://github.com/sneurlax/socks_socket"
|
||||||
|
source: git
|
||||||
|
version: "1.1.1"
|
||||||
source_gen:
|
source_gen:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -765,6 +775,15 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.2"
|
version: "1.0.2"
|
||||||
|
tor_binary:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
path: "."
|
||||||
|
ref: cb811c610871a9517d47134b87c2f590c15c96c5
|
||||||
|
resolved-ref: cb811c610871a9517d47134b87c2f590c15c96c5
|
||||||
|
url: "https://github.com/MrCyjaneK/flutter-tor_binary"
|
||||||
|
source: git
|
||||||
|
version: "4.7.14"
|
||||||
tuple:
|
tuple:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
@ -70,7 +70,10 @@ class WalletKeysAndSeedPageRobot {
|
||||||
if (walletType == WalletType.bitcoin ||
|
if (walletType == WalletType.bitcoin ||
|
||||||
walletType == WalletType.litecoin ||
|
walletType == WalletType.litecoin ||
|
||||||
walletType == WalletType.bitcoinCash) {
|
walletType == WalletType.bitcoinCash) {
|
||||||
commonTestCases.hasText(appStore.wallet!.seed!);
|
final seedWords = appStore.wallet!.seed!.split(" ");
|
||||||
|
for (var seedWord in seedWords) {
|
||||||
|
commonTestCases.hasTextAtLestOnce(seedWord);
|
||||||
|
}
|
||||||
tester.printToConsole('$walletName wallet has seeds properly displayed');
|
tester.printToConsole('$walletName wallet has seeds properly displayed');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,10 +81,14 @@ class WalletKeysAndSeedPageRobot {
|
||||||
walletType == WalletType.solana ||
|
walletType == WalletType.solana ||
|
||||||
walletType == WalletType.tron) {
|
walletType == WalletType.tron) {
|
||||||
if (hasSeed) {
|
if (hasSeed) {
|
||||||
commonTestCases.hasText(appStore.wallet!.seed!);
|
final seedWords = appStore.wallet!.seed!.split(" ");
|
||||||
|
for (var seedWord in seedWords) {
|
||||||
|
commonTestCases.hasTextAtLestOnce(seedWord);
|
||||||
|
}
|
||||||
tester.printToConsole('$walletName wallet has seeds properly displayed');
|
tester.printToConsole('$walletName wallet has seeds properly displayed');
|
||||||
}
|
}
|
||||||
if (hasPrivateKey) {
|
if (hasPrivateKey) {
|
||||||
|
await commonTestCases.tapItemByKey('wallet_keys_page_keys');
|
||||||
commonTestCases.hasText(appStore.wallet!.privateKey!);
|
commonTestCases.hasText(appStore.wallet!.privateKey!);
|
||||||
tester.printToConsole('$walletName wallet has private key properly displayed');
|
tester.printToConsole('$walletName wallet has private key properly displayed');
|
||||||
}
|
}
|
||||||
|
@ -89,14 +96,19 @@ class WalletKeysAndSeedPageRobot {
|
||||||
|
|
||||||
if (walletType == WalletType.nano || walletType == WalletType.banano) {
|
if (walletType == WalletType.nano || walletType == WalletType.banano) {
|
||||||
if (hasSeed) {
|
if (hasSeed) {
|
||||||
commonTestCases.hasText(appStore.wallet!.seed!);
|
final seedWords = appStore.wallet!.seed!.split(" ");
|
||||||
|
for (var seedWord in seedWords) {
|
||||||
|
commonTestCases.hasTextAtLestOnce(seedWord);
|
||||||
|
}
|
||||||
tester.printToConsole('$walletName wallet has seeds properly displayed');
|
tester.printToConsole('$walletName wallet has seeds properly displayed');
|
||||||
}
|
}
|
||||||
if (hasHexSeed) {
|
if (hasHexSeed) {
|
||||||
|
await commonTestCases.tapItemByKey('wallet_keys_page_keys');
|
||||||
commonTestCases.hasText(appStore.wallet!.hexSeed!);
|
commonTestCases.hasText(appStore.wallet!.hexSeed!);
|
||||||
tester.printToConsole('$walletName wallet has hexSeed properly displayed');
|
tester.printToConsole('$walletName wallet has hexSeed properly displayed');
|
||||||
}
|
}
|
||||||
if (hasPrivateKey) {
|
if (hasPrivateKey) {
|
||||||
|
await commonTestCases.tapItemByKey('wallet_keys_page_keys');
|
||||||
commonTestCases.hasText(appStore.wallet!.privateKey!);
|
commonTestCases.hasText(appStore.wallet!.privateKey!);
|
||||||
tester.printToConsole('$walletName wallet has private key properly displayed');
|
tester.printToConsole('$walletName wallet has private key properly displayed');
|
||||||
}
|
}
|
||||||
|
@ -129,35 +141,39 @@ class WalletKeysAndSeedPageRobot {
|
||||||
final hasSeedLegacy = Polyseed.isValidSeed(seed);
|
final hasSeedLegacy = Polyseed.isValidSeed(seed);
|
||||||
|
|
||||||
if (hasPublicSpendKey) {
|
if (hasPublicSpendKey) {
|
||||||
|
await commonTestCases.tapItemByKey('wallet_keys_page_keys');
|
||||||
commonTestCases.hasText(keys.publicSpendKey);
|
commonTestCases.hasText(keys.publicSpendKey);
|
||||||
tester.printToConsole('$walletName wallet has public spend key properly displayed');
|
tester.printToConsole('$walletName wallet has public spend key properly displayed');
|
||||||
}
|
}
|
||||||
if (hasPrivateSpendKey) {
|
if (hasPrivateSpendKey) {
|
||||||
|
await commonTestCases.tapItemByKey('wallet_keys_page_keys');
|
||||||
commonTestCases.hasText(keys.privateSpendKey);
|
commonTestCases.hasText(keys.privateSpendKey);
|
||||||
tester.printToConsole('$walletName wallet has private spend key properly displayed');
|
tester.printToConsole('$walletName wallet has private spend key properly displayed');
|
||||||
}
|
}
|
||||||
if (hasPublicViewKey) {
|
if (hasPublicViewKey) {
|
||||||
|
await commonTestCases.tapItemByKey('wallet_keys_page_keys');
|
||||||
commonTestCases.hasText(keys.publicViewKey);
|
commonTestCases.hasText(keys.publicViewKey);
|
||||||
tester.printToConsole('$walletName wallet has public view key properly displayed');
|
tester.printToConsole('$walletName wallet has public view key properly displayed');
|
||||||
}
|
}
|
||||||
if (hasPrivateViewKey) {
|
if (hasPrivateViewKey) {
|
||||||
|
await commonTestCases.tapItemByKey('wallet_keys_page_keys');
|
||||||
commonTestCases.hasText(keys.privateViewKey);
|
commonTestCases.hasText(keys.privateViewKey);
|
||||||
tester.printToConsole('$walletName wallet has private view key properly displayed');
|
tester.printToConsole('$walletName wallet has private view key properly displayed');
|
||||||
}
|
}
|
||||||
if (hasSeeds) {
|
if (hasSeeds) {
|
||||||
await commonTestCases.dragUntilVisible(
|
await commonTestCases.tapItemByKey('wallet_keys_page_seed');
|
||||||
'${walletName}_wallet_seed_item_key',
|
final seedWords = seed.split(" ");
|
||||||
'wallet_keys_page_credentials_list_view_key',
|
for (var seedWord in seedWords) {
|
||||||
);
|
commonTestCases.hasTextAtLestOnce(seedWord);
|
||||||
commonTestCases.hasText(seed);
|
}
|
||||||
tester.printToConsole('$walletName wallet has seeds properly displayed');
|
tester.printToConsole('$walletName wallet has seeds properly displayed');
|
||||||
}
|
}
|
||||||
if (hasSeedLegacy) {
|
if (hasSeedLegacy) {
|
||||||
await commonTestCases.dragUntilVisible(
|
await commonTestCases.tapItemByKey('wallet_keys_page_seed_legacy');
|
||||||
'${walletName}_wallet_seed_legacy_item_key',
|
final seedWords = legacySeed.split(" ");
|
||||||
'wallet_keys_page_credentials_list_view_key',
|
for (var seedWord in seedWords) {
|
||||||
);
|
commonTestCases.hasTextAtLestOnce(seedWord);
|
||||||
commonTestCases.hasText(legacySeed);
|
}
|
||||||
tester.printToConsole('$walletName wallet has legacy seeds properly displayed');
|
tester.printToConsole('$walletName wallet has legacy seeds properly displayed');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,8 +6,8 @@ import 'package:cake_wallet/anonpay/anonpay_status_response.dart';
|
||||||
import 'package:cake_wallet/core/fiat_conversion_service.dart';
|
import 'package:cake_wallet/core/fiat_conversion_service.dart';
|
||||||
import 'package:cake_wallet/entities/fiat_currency.dart';
|
import 'package:cake_wallet/entities/fiat_currency.dart';
|
||||||
import 'package:cake_wallet/exchange/limits.dart';
|
import 'package:cake_wallet/exchange/limits.dart';
|
||||||
|
import 'package:cw_core/utils/proxy_wrapper.dart';
|
||||||
import 'package:cw_core/wallet_base.dart';
|
import 'package:cw_core/wallet_base.dart';
|
||||||
import 'package:http/http.dart';
|
|
||||||
import 'package:cw_core/crypto_currency.dart';
|
import 'package:cw_core/crypto_currency.dart';
|
||||||
import 'package:cake_wallet/.secrets.g.dart' as secrets;
|
import 'package:cake_wallet/.secrets.g.dart' as secrets;
|
||||||
|
|
||||||
|
@ -20,8 +20,9 @@ class AnonPayApi {
|
||||||
final WalletBase wallet;
|
final WalletBase wallet;
|
||||||
|
|
||||||
static const anonpayRef = secrets.anonPayReferralCode;
|
static const anonpayRef = secrets.anonPayReferralCode;
|
||||||
static const onionApiAuthority = 'tqzngtf2hybjbexznel6dhgsvbynjzezoybvtv6iofomx7gchqfssgqd.onion';
|
|
||||||
static const clearNetAuthority = 'trocador.app';
|
static const clearNetAuthority = 'trocador.app';
|
||||||
|
// static const onionApiAuthority = 'tqzngtf2hybjbexznel6dhgsvbynjzezoybvtv6iofomx7gchqfssgqd.onion';
|
||||||
|
static const onionApiAuthority = clearNetAuthority;
|
||||||
static const markup = secrets.trocadorExchangeMarkup;
|
static const markup = secrets.trocadorExchangeMarkup;
|
||||||
static const anonPayPath = '/anonpay';
|
static const anonPayPath = '/anonpay';
|
||||||
static const anonPayStatus = '/anonpay/status';
|
static const anonPayStatus = '/anonpay/status';
|
||||||
|
@ -29,8 +30,11 @@ class AnonPayApi {
|
||||||
static const apiKey = secrets.trocadorApiKey;
|
static const apiKey = secrets.trocadorApiKey;
|
||||||
|
|
||||||
Future<AnonpayStatusResponse> paymentStatus(String id) async {
|
Future<AnonpayStatusResponse> paymentStatus(String id) async {
|
||||||
final authority = await _getAuthority();
|
final response = await ProxyWrapper().get(
|
||||||
final response = await get(Uri.https(authority, "$anonPayStatus/$id"));
|
clearnetUri: Uri.https(clearNetAuthority, "$anonPayStatus/$id"),
|
||||||
|
onionUri: Uri.https(onionApiAuthority, "$anonPayStatus/$id"),
|
||||||
|
);
|
||||||
|
|
||||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||||
final status = responseJSON['Status'] as String;
|
final status = responseJSON['Status'] as String;
|
||||||
final fiatAmount = responseJSON['Fiat_Amount'] as double?;
|
final fiatAmount = responseJSON['Fiat_Amount'] as double?;
|
||||||
|
@ -69,10 +73,11 @@ class AnonPayApi {
|
||||||
if (request.fiatEquivalent != null) {
|
if (request.fiatEquivalent != null) {
|
||||||
body['fiat_equiv'] = request.fiatEquivalent;
|
body['fiat_equiv'] = request.fiatEquivalent;
|
||||||
}
|
}
|
||||||
final authority = await _getAuthority();
|
final response = await ProxyWrapper().get(
|
||||||
|
clearnetUri: Uri.https(clearNetAuthority, anonPayPath, body),
|
||||||
final response = await get(Uri.https(authority, anonPayPath, body));
|
onionUri: Uri.https(onionApiAuthority, anonPayPath, body),
|
||||||
|
);
|
||||||
|
|
||||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||||
final id = responseJSON['ID'] as String;
|
final id = responseJSON['ID'] as String;
|
||||||
final url = responseJSON['url'] as String;
|
final url = responseJSON['url'] as String;
|
||||||
|
@ -146,17 +151,16 @@ class AnonPayApi {
|
||||||
'name': cryptoCurrency.name,
|
'name': cryptoCurrency.name,
|
||||||
};
|
};
|
||||||
|
|
||||||
final String apiAuthority = await _getAuthority();
|
final response = await ProxyWrapper().get(
|
||||||
final uri = Uri.https(apiAuthority, coinPath, params);
|
clearnetUri: Uri.https(clearNetAuthority, coinPath, params),
|
||||||
|
onionUri: Uri.https(onionApiAuthority, coinPath, params),
|
||||||
final response = await get(uri);
|
);
|
||||||
|
|
||||||
|
final responseJSON = json.decode(response.body) as List<dynamic>;
|
||||||
if (response.statusCode != 200) {
|
if (response.statusCode != 200) {
|
||||||
throw Exception('Unexpected http status: ${response.statusCode}');
|
throw Exception('Unexpected http status: ${response.statusCode}');
|
||||||
}
|
}
|
||||||
|
|
||||||
final responseJSON = json.decode(response.body) as List<dynamic>;
|
|
||||||
|
|
||||||
if (responseJSON.isEmpty) {
|
if (responseJSON.isEmpty) {
|
||||||
throw Exception('No data');
|
throw Exception('No data');
|
||||||
}
|
}
|
||||||
|
@ -197,17 +201,4 @@ class AnonPayApi {
|
||||||
return tag.toLowerCase();
|
return tag.toLowerCase();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<String> _getAuthority() async {
|
|
||||||
try {
|
|
||||||
if (useTorOnly) {
|
|
||||||
return onionApiAuthority;
|
|
||||||
}
|
|
||||||
final uri = Uri.https(onionApiAuthority, '/anonpay');
|
|
||||||
await get(uri);
|
|
||||||
return onionApiAuthority;
|
|
||||||
} catch (e) {
|
|
||||||
return clearNetAuthority;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'package:cake_wallet/anypay/any_pay_payment_committed_info.dart';
|
import 'package:cake_wallet/anypay/any_pay_payment_committed_info.dart';
|
||||||
import 'package:cake_wallet/utils/exception_handler.dart';
|
import 'package:cake_wallet/utils/exception_handler.dart';
|
||||||
|
import 'package:cw_core/utils/proxy_wrapper.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:http/http.dart';
|
|
||||||
import 'package:cw_core/crypto_currency.dart';
|
import 'package:cw_core/crypto_currency.dart';
|
||||||
import 'package:cake_wallet/anypay/any_pay_payment.dart';
|
import 'package:cake_wallet/anypay/any_pay_payment.dart';
|
||||||
import 'package:cake_wallet/anypay/any_pay_trasnaction.dart';
|
import 'package:cake_wallet/anypay/any_pay_trasnaction.dart';
|
||||||
|
@ -53,8 +53,12 @@ class AnyPayApi {
|
||||||
final body = <String, dynamic>{
|
final body = <String, dynamic>{
|
||||||
'chain': chainByScheme(scheme),
|
'chain': chainByScheme(scheme),
|
||||||
'currency': currencyByScheme(scheme).title};
|
'currency': currencyByScheme(scheme).title};
|
||||||
final response = await post(url, headers: headers, body: utf8.encode(json.encode(body)));
|
final response = await ProxyWrapper().post(
|
||||||
|
clearnetUri: url,
|
||||||
|
headers: headers,
|
||||||
|
body: json.encode(body),
|
||||||
|
);
|
||||||
|
|
||||||
if (response.statusCode != 200) {
|
if (response.statusCode != 200) {
|
||||||
await ExceptionHandler.onError(FlutterErrorDetails(exception: response));
|
await ExceptionHandler.onError(FlutterErrorDetails(exception: response));
|
||||||
throw Exception('Unexpected response http code: ${response.statusCode}');
|
throw Exception('Unexpected response http code: ${response.statusCode}');
|
||||||
|
@ -79,7 +83,12 @@ class AnyPayApi {
|
||||||
'chain': chain,
|
'chain': chain,
|
||||||
'currency': currency,
|
'currency': currency,
|
||||||
'transactions': transactions.map((tx) => {'tx': tx.tx, 'tx_hash': tx.id, 'tx_key': tx.key}).toList()};
|
'transactions': transactions.map((tx) => {'tx': tx.tx, 'tx_hash': tx.id, 'tx_key': tx.key}).toList()};
|
||||||
final response = await post(Uri.parse(uri), headers: headers, body: utf8.encode(json.encode(body)));
|
final response = await ProxyWrapper().post(
|
||||||
|
clearnetUri: Uri.parse(uri),
|
||||||
|
headers: headers,
|
||||||
|
body: json.encode(body),
|
||||||
|
);
|
||||||
|
|
||||||
if (response.statusCode == 400) {
|
if (response.statusCode == 400) {
|
||||||
final decodedBody = json.decode(response.body) as Map<String, dynamic>;
|
final decodedBody = json.decode(response.body) as Map<String, dynamic>;
|
||||||
throw Exception(decodedBody['message'] as String? ?? 'Unexpected response\nError code: 400');
|
throw Exception(decodedBody['message'] as String? ?? 'Unexpected response\nError code: 400');
|
||||||
|
|
|
@ -10,6 +10,7 @@ import 'package:cake_wallet/generated/i18n.dart';
|
||||||
import 'package:cake_wallet/routes.dart';
|
import 'package:cake_wallet/routes.dart';
|
||||||
import 'package:cake_wallet/src/screens/connect_device/connect_device_page.dart';
|
import 'package:cake_wallet/src/screens/connect_device/connect_device_page.dart';
|
||||||
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
|
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
|
||||||
|
import 'package:cw_core/utils/proxy_wrapper.dart';
|
||||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||||
import 'package:cake_wallet/view_model/hardware_wallet/ledger_view_model.dart';
|
import 'package:cake_wallet/view_model/hardware_wallet/ledger_view_model.dart';
|
||||||
import 'package:cw_core/crypto_currency.dart';
|
import 'package:cw_core/crypto_currency.dart';
|
||||||
|
@ -17,7 +18,6 @@ import 'package:cw_core/utils/print_verbose.dart';
|
||||||
import 'package:cw_core/wallet_base.dart';
|
import 'package:cw_core/wallet_base.dart';
|
||||||
import 'package:cw_core/wallet_type.dart';
|
import 'package:cw_core/wallet_type.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:http/http.dart' as http;
|
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
class DFXBuyProvider extends BuyProvider {
|
class DFXBuyProvider extends BuyProvider {
|
||||||
|
@ -100,11 +100,12 @@ class DFXBuyProvider extends BuyProvider {
|
||||||
});
|
});
|
||||||
|
|
||||||
final uri = Uri.https(_baseUrl, _authPath);
|
final uri = Uri.https(_baseUrl, _authPath);
|
||||||
var response = await http.post(
|
final response = await ProxyWrapper().post(
|
||||||
uri,
|
clearnetUri: uri,
|
||||||
headers: {'Content-Type': 'application/json'},
|
headers: {'Content-Type': 'application/json'},
|
||||||
body: requestBody,
|
body: requestBody,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
if (response.statusCode == 201) {
|
if (response.statusCode == 201) {
|
||||||
final responseBody = jsonDecode(response.body);
|
final responseBody = jsonDecode(response.body);
|
||||||
|
@ -137,8 +138,10 @@ class DFXBuyProvider extends BuyProvider {
|
||||||
final url = Uri.https(_baseUrl, '/v1/fiat');
|
final url = Uri.https(_baseUrl, '/v1/fiat');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final response = await http.get(url, headers: {'accept': 'application/json'});
|
final response = await ProxyWrapper().get(
|
||||||
|
clearnetUri: url,
|
||||||
|
headers: {'accept': 'application/json'});
|
||||||
|
|
||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
final data = jsonDecode(response.body) as List<dynamic>;
|
final data = jsonDecode(response.body) as List<dynamic>;
|
||||||
for (final item in data) {
|
for (final item in data) {
|
||||||
|
@ -160,8 +163,8 @@ class DFXBuyProvider extends BuyProvider {
|
||||||
final url = Uri.https(_baseUrl, '/v1/asset', {'blockchains': blockchain});
|
final url = Uri.https(_baseUrl, '/v1/asset', {'blockchains': blockchain});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final response = await http.get(url, headers: {'accept': 'application/json'});
|
final response = await ProxyWrapper().get(clearnetUri: url, headers: {'accept': 'application/json'});
|
||||||
|
|
||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
final responseData = jsonDecode(response.body);
|
final responseData = jsonDecode(response.body);
|
||||||
|
|
||||||
|
@ -271,7 +274,12 @@ class DFXBuyProvider extends BuyProvider {
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final response = await http.put(url, headers: headers, body: body);
|
final response = await ProxyWrapper().put(
|
||||||
|
clearnetUri: url,
|
||||||
|
headers: headers,
|
||||||
|
body: body,
|
||||||
|
);
|
||||||
|
|
||||||
final responseData = jsonDecode(response.body);
|
final responseData = jsonDecode(response.body);
|
||||||
|
|
||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
|
|
|
@ -9,10 +9,10 @@ import 'package:cake_wallet/entities/fiat_currency.dart';
|
||||||
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
|
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
|
||||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||||
import 'package:cw_core/crypto_currency.dart';
|
import 'package:cw_core/crypto_currency.dart';
|
||||||
|
import 'package:cw_core/utils/proxy_wrapper.dart';
|
||||||
import 'package:cw_core/wallet_base.dart';
|
import 'package:cw_core/wallet_base.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'dart:developer';
|
import 'dart:developer';
|
||||||
import 'package:http/http.dart' as http;
|
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
class KryptonimBuyProvider extends BuyProvider {
|
class KryptonimBuyProvider extends BuyProvider {
|
||||||
|
@ -74,9 +74,14 @@ class KryptonimBuyProvider extends BuyProvider {
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final response = await http.post(url, headers: headers, body: body);
|
final response = await ProxyWrapper().post(
|
||||||
|
clearnetUri: url,
|
||||||
|
headers: headers,
|
||||||
|
body: body,
|
||||||
|
);
|
||||||
|
|
||||||
if (response.statusCode == 200 || response.statusCode == 201 || response.statusCode == 401) {
|
if (response.statusCode == 200 || response.statusCode == 201 || response.statusCode == 401) {
|
||||||
|
|
||||||
return jsonDecode(response.body) as Map<String, dynamic>;
|
return jsonDecode(response.body) as Map<String, dynamic>;
|
||||||
} else {
|
} else {
|
||||||
return {};
|
return {};
|
||||||
|
|
|
@ -8,13 +8,13 @@ import 'package:cake_wallet/buy/payment_method.dart';
|
||||||
import 'package:cake_wallet/entities/fiat_currency.dart';
|
import 'package:cake_wallet/entities/fiat_currency.dart';
|
||||||
import 'package:cake_wallet/generated/i18n.dart';
|
import 'package:cake_wallet/generated/i18n.dart';
|
||||||
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
|
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
|
||||||
|
import 'package:cw_core/utils/proxy_wrapper.dart';
|
||||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||||
import 'package:cw_core/crypto_currency.dart';
|
import 'package:cw_core/crypto_currency.dart';
|
||||||
import 'package:cw_core/utils/print_verbose.dart';
|
import 'package:cw_core/utils/print_verbose.dart';
|
||||||
import 'package:cw_core/wallet_base.dart';
|
import 'package:cw_core/wallet_base.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'dart:developer';
|
import 'dart:developer';
|
||||||
import 'package:http/http.dart' as http;
|
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
class MeldBuyProvider extends BuyProvider {
|
class MeldBuyProvider extends BuyProvider {
|
||||||
|
@ -71,8 +71,8 @@ class MeldBuyProvider extends BuyProvider {
|
||||||
final url = Uri.https(_baseUrl, path, params);
|
final url = Uri.https(_baseUrl, path, params);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final response = await http.get(
|
final response = await ProxyWrapper().get(
|
||||||
url,
|
clearnetUri: url,
|
||||||
headers: {
|
headers: {
|
||||||
'Authorization': _isProduction ? '' : _testApiKey,
|
'Authorization': _isProduction ? '' : _testApiKey,
|
||||||
'Meld-Version': '2023-12-19',
|
'Meld-Version': '2023-12-19',
|
||||||
|
@ -80,6 +80,7 @@ class MeldBuyProvider extends BuyProvider {
|
||||||
'content-type': 'application/json',
|
'content-type': 'application/json',
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
final data = jsonDecode(response.body) as List<dynamic>;
|
final data = jsonDecode(response.body) as List<dynamic>;
|
||||||
|
@ -130,7 +131,12 @@ class MeldBuyProvider extends BuyProvider {
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final response = await http.post(url, headers: headers, body: body);
|
final response = await ProxyWrapper().post(
|
||||||
|
clearnetUri: url,
|
||||||
|
headers: headers,
|
||||||
|
body: body,
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
final data = jsonDecode(response.body) as Map<String, dynamic>;
|
final data = jsonDecode(response.body) as Map<String, dynamic>;
|
||||||
|
|
|
@ -16,12 +16,12 @@ import 'package:cake_wallet/palette.dart';
|
||||||
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
|
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
|
||||||
import 'package:cake_wallet/store/app_store.dart';
|
import 'package:cake_wallet/store/app_store.dart';
|
||||||
import 'package:cake_wallet/themes/core/material_base_theme.dart';
|
import 'package:cake_wallet/themes/core/material_base_theme.dart';
|
||||||
|
import 'package:cw_core/utils/proxy_wrapper.dart';
|
||||||
import 'package:cw_core/crypto_currency.dart';
|
import 'package:cw_core/crypto_currency.dart';
|
||||||
import 'package:cw_core/wallet_base.dart';
|
import 'package:cw_core/wallet_base.dart';
|
||||||
import 'package:cw_core/wallet_type.dart';
|
import 'package:cw_core/wallet_type.dart';
|
||||||
import 'package:cw_core/utils/print_verbose.dart';
|
import 'package:cw_core/utils/print_verbose.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:http/http.dart';
|
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
class MoonPayProvider extends BuyProvider {
|
class MoonPayProvider extends BuyProvider {
|
||||||
|
@ -98,9 +98,12 @@ class MoonPayProvider extends BuyProvider {
|
||||||
Future<String> getMoonpaySignature(String query) async {
|
Future<String> getMoonpaySignature(String query) async {
|
||||||
final uri = Uri.https(_cIdBaseUrl, "/api/moonpay");
|
final uri = Uri.https(_cIdBaseUrl, "/api/moonpay");
|
||||||
|
|
||||||
final response = await post(uri,
|
final response = await ProxyWrapper().post(
|
||||||
headers: {'Content-Type': 'application/json', 'x-api-key': _exchangeHelperApiKey},
|
clearnetUri: uri,
|
||||||
body: json.encode({'query': query}));
|
headers: {'Content-Type': 'application/json', 'x-api-key': _exchangeHelperApiKey},
|
||||||
|
body: json.encode({'query': query}),
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
return (jsonDecode(response.body) as Map<String, dynamic>)['signature'] as String;
|
return (jsonDecode(response.body) as Map<String, dynamic>)['signature'] as String;
|
||||||
|
@ -120,7 +123,11 @@ class MoonPayProvider extends BuyProvider {
|
||||||
final url = Uri.https(_baseUrl, path, params);
|
final url = Uri.https(_baseUrl, path, params);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final response = await get(url, headers: {'accept': 'application/json'});
|
final response = await ProxyWrapper().get(
|
||||||
|
clearnetUri: url,
|
||||||
|
headers: {'accept': 'application/json'},
|
||||||
|
);
|
||||||
|
|
||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
return jsonDecode(response.body) as Map<String, dynamic>;
|
return jsonDecode(response.body) as Map<String, dynamic>;
|
||||||
} else {
|
} else {
|
||||||
|
@ -191,8 +198,8 @@ class MoonPayProvider extends BuyProvider {
|
||||||
final path = '$_currenciesPath/$formattedCryptoCurrency$quotePath';
|
final path = '$_currenciesPath/$formattedCryptoCurrency$quotePath';
|
||||||
final url = Uri.https(_baseUrl, path, params);
|
final url = Uri.https(_baseUrl, path, params);
|
||||||
try {
|
try {
|
||||||
final response = await get(url);
|
final response = await ProxyWrapper().get(clearnetUri: url);
|
||||||
|
|
||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
final data = jsonDecode(response.body) as Map<String, dynamic>;
|
final data = jsonDecode(response.body) as Map<String, dynamic>;
|
||||||
|
|
||||||
|
@ -306,7 +313,8 @@ class MoonPayProvider extends BuyProvider {
|
||||||
Future<Order> findOrderById(String id) async {
|
Future<Order> findOrderById(String id) async {
|
||||||
final url = _apiUrl + _transactionsSuffix + '/$id' + '?apiKey=' + _apiKey;
|
final url = _apiUrl + _transactionsSuffix + '/$id' + '?apiKey=' + _apiKey;
|
||||||
final uri = Uri.parse(url);
|
final uri = Uri.parse(url);
|
||||||
final response = await get(uri);
|
final response = await ProxyWrapper().get(clearnetUri: uri);
|
||||||
|
|
||||||
|
|
||||||
if (response.statusCode != 200) {
|
if (response.statusCode != 200) {
|
||||||
throw BuyException(title: providerDescription, content: 'Transaction $id is not found!');
|
throw BuyException(title: providerDescription, content: 'Transaction $id is not found!');
|
||||||
|
|
|
@ -9,11 +9,12 @@ import 'package:cake_wallet/buy/payment_method.dart';
|
||||||
import 'package:cake_wallet/entities/fiat_currency.dart';
|
import 'package:cake_wallet/entities/fiat_currency.dart';
|
||||||
import 'package:cake_wallet/generated/i18n.dart';
|
import 'package:cake_wallet/generated/i18n.dart';
|
||||||
import 'package:cake_wallet/themes/core/theme_store.dart';
|
import 'package:cake_wallet/themes/core/theme_store.dart';
|
||||||
|
import 'package:cake_wallet/store/settings_store.dart';
|
||||||
|
import 'package:cw_core/utils/proxy_wrapper.dart';
|
||||||
import 'package:cw_core/crypto_currency.dart';
|
import 'package:cw_core/crypto_currency.dart';
|
||||||
import 'package:cw_core/utils/print_verbose.dart';
|
import 'package:cw_core/utils/print_verbose.dart';
|
||||||
import 'package:cw_core/wallet_base.dart';
|
import 'package:cw_core/wallet_base.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:http/http.dart' as http;
|
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
class OnRamperBuyProvider extends BuyProvider {
|
class OnRamperBuyProvider extends BuyProvider {
|
||||||
|
@ -66,8 +67,10 @@ class OnRamperBuyProvider extends BuyProvider {
|
||||||
final url = Uri.https(_baseApiUrl, '$supported$defaultsAll', params);
|
final url = Uri.https(_baseApiUrl, '$supported$defaultsAll', params);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final response =
|
final response = await ProxyWrapper().get(
|
||||||
await http.get(url, headers: {'Authorization': _apiKey, 'accept': 'application/json'});
|
clearnetUri: url,
|
||||||
|
headers: {'Authorization': _apiKey, 'accept': 'application/json'},
|
||||||
|
);
|
||||||
|
|
||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
final Map<String, dynamic> data = jsonDecode(response.body) as Map<String, dynamic>;
|
final Map<String, dynamic> data = jsonDecode(response.body) as Map<String, dynamic>;
|
||||||
|
@ -102,8 +105,8 @@ class OnRamperBuyProvider extends BuyProvider {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final response =
|
final response =
|
||||||
await http.get(url, headers: {'Authorization': _apiKey, 'accept': 'application/json'});
|
await ProxyWrapper().get(clearnetUri: url, headers: {'Authorization': _apiKey, 'accept': 'application/json'});
|
||||||
|
|
||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
final Map<String, dynamic> data = jsonDecode(response.body) as Map<String, dynamic>;
|
final Map<String, dynamic> data = jsonDecode(response.body) as Map<String, dynamic>;
|
||||||
final List<dynamic> message = data['message'] as List<dynamic>;
|
final List<dynamic> message = data['message'] as List<dynamic>;
|
||||||
|
@ -132,7 +135,8 @@ class OnRamperBuyProvider extends BuyProvider {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final response =
|
final response =
|
||||||
await http.get(url, headers: {'Authorization': _apiKey, 'accept': 'application/json'});
|
await ProxyWrapper().get(clearnetUri: url, headers: {'Authorization': _apiKey, 'accept': 'application/json'});
|
||||||
|
|
||||||
|
|
||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
final Map<String, dynamic> data = jsonDecode(response.body) as Map<String, dynamic>;
|
final Map<String, dynamic> data = jsonDecode(response.body) as Map<String, dynamic>;
|
||||||
|
@ -195,8 +199,8 @@ class OnRamperBuyProvider extends BuyProvider {
|
||||||
final headers = {'Authorization': _apiKey, 'accept': 'application/json'};
|
final headers = {'Authorization': _apiKey, 'accept': 'application/json'};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final response = await http.get(url, headers: headers);
|
final response = await ProxyWrapper().get(clearnetUri: url, headers: headers);
|
||||||
|
|
||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
final data = jsonDecode(response.body) as List<dynamic>;
|
final data = jsonDecode(response.body) as List<dynamic>;
|
||||||
if (data.isEmpty) return null;
|
if (data.isEmpty) return null;
|
||||||
|
|
|
@ -11,6 +11,7 @@ import 'package:cake_wallet/generated/i18n.dart';
|
||||||
import 'package:cake_wallet/routes.dart';
|
import 'package:cake_wallet/routes.dart';
|
||||||
import 'package:cake_wallet/src/screens/connect_device/connect_device_page.dart';
|
import 'package:cake_wallet/src/screens/connect_device/connect_device_page.dart';
|
||||||
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
|
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
|
||||||
|
import 'package:cw_core/utils/proxy_wrapper.dart';
|
||||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||||
import 'package:cake_wallet/view_model/hardware_wallet/ledger_view_model.dart';
|
import 'package:cake_wallet/view_model/hardware_wallet/ledger_view_model.dart';
|
||||||
import 'package:cw_core/crypto_currency.dart';
|
import 'package:cw_core/crypto_currency.dart';
|
||||||
|
@ -18,7 +19,6 @@ import 'package:cw_core/utils/print_verbose.dart';
|
||||||
import 'package:cw_core/wallet_base.dart';
|
import 'package:cw_core/wallet_base.dart';
|
||||||
import 'package:cw_core/wallet_type.dart';
|
import 'package:cw_core/wallet_type.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:http/http.dart' as http;
|
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
class RobinhoodBuyProvider extends BuyProvider {
|
class RobinhoodBuyProvider extends BuyProvider {
|
||||||
|
@ -69,7 +69,8 @@ class RobinhoodBuyProvider extends BuyProvider {
|
||||||
final uri = Uri.https(_apiBaseUrl, '$_assetsPath', {'applicationId': _applicationId});
|
final uri = Uri.https(_apiBaseUrl, '$_assetsPath', {'applicationId': _applicationId});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final response = await http.get(uri, headers: {'accept': 'application/json'});
|
final response = await ProxyWrapper().get(clearnetUri: uri, headers: {'accept': 'application/json'});
|
||||||
|
|
||||||
|
|
||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
final responseData = jsonDecode(response.body) as Map<String, dynamic>;
|
final responseData = jsonDecode(response.body) as Map<String, dynamic>;
|
||||||
|
@ -122,12 +123,14 @@ class RobinhoodBuyProvider extends BuyProvider {
|
||||||
|
|
||||||
final uri = Uri.https(_cIdBaseUrl, "/api/robinhood");
|
final uri = Uri.https(_cIdBaseUrl, "/api/robinhood");
|
||||||
|
|
||||||
var response = await http.post(uri,
|
var response = await ProxyWrapper().post(
|
||||||
headers: {'Content-Type': 'application/json'},
|
clearnetUri: uri,
|
||||||
body: json
|
headers: {'Content-Type': 'application/json'},
|
||||||
.encode({'valid_until': valid_until, 'wallet': walletAddress, 'signature': signature}));
|
body: json.encode({'valid_until': valid_until, 'wallet': walletAddress, 'signature': signature}),
|
||||||
|
);
|
||||||
|
|
||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
|
|
||||||
return (jsonDecode(response.body) as Map<String, dynamic>)['connectId'] as String;
|
return (jsonDecode(response.body) as Map<String, dynamic>)['connectId'] as String;
|
||||||
} else {
|
} else {
|
||||||
throw Exception('Provider currently unavailable. Status: ${response.statusCode}');
|
throw Exception('Provider currently unavailable. Status: ${response.statusCode}');
|
||||||
|
@ -219,7 +222,8 @@ class RobinhoodBuyProvider extends BuyProvider {
|
||||||
Uri.https('api.robinhood.com', '/catpay/v1/${cryptoCurrency.title}/quote/', queryParams);
|
Uri.https('api.robinhood.com', '/catpay/v1/${cryptoCurrency.title}/quote/', queryParams);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final response = await http.get(uri, headers: {'accept': 'application/json'});
|
final response = await ProxyWrapper().get(clearnetUri: uri, headers: {'accept': 'application/json'});
|
||||||
|
|
||||||
final responseData = jsonDecode(response.body) as Map<String, dynamic>;
|
final responseData = jsonDecode(response.body) as Map<String, dynamic>;
|
||||||
|
|
||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
|
|
|
@ -3,7 +3,7 @@ import 'package:cake_wallet/buy/buy_exception.dart';
|
||||||
import 'package:cake_wallet/buy/pairs_utils.dart';
|
import 'package:cake_wallet/buy/pairs_utils.dart';
|
||||||
import 'package:cake_wallet/entities/fiat_currency.dart';
|
import 'package:cake_wallet/entities/fiat_currency.dart';
|
||||||
import 'package:cw_core/crypto_currency.dart';
|
import 'package:cw_core/crypto_currency.dart';
|
||||||
import 'package:http/http.dart';
|
import 'package:cw_core/utils/proxy_wrapper.dart';
|
||||||
import 'package:cake_wallet/buy/buy_amount.dart';
|
import 'package:cake_wallet/buy/buy_amount.dart';
|
||||||
import 'package:cake_wallet/buy/buy_provider.dart';
|
import 'package:cake_wallet/buy/buy_provider.dart';
|
||||||
import 'package:cake_wallet/buy/buy_provider_description.dart';
|
import 'package:cake_wallet/buy/buy_provider_description.dart';
|
||||||
|
@ -73,18 +73,21 @@ class WyreBuyProvider extends BuyProvider {
|
||||||
'referrerAccountId': _accountId,
|
'referrerAccountId': _accountId,
|
||||||
'lockFields': ['amount', 'sourceCurrency', 'destCurrency', 'dest']
|
'lockFields': ['amount', 'sourceCurrency', 'destCurrency', 'dest']
|
||||||
};
|
};
|
||||||
final response = await post(uri,
|
final response = await ProxyWrapper().post(
|
||||||
headers: {
|
clearnetUri: uri,
|
||||||
'Authorization': 'Bearer $_secretKey',
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Authorization': 'Bearer $_secretKey',
|
||||||
'cache-control': 'no-cache'
|
'Content-Type': 'application/json',
|
||||||
},
|
'cache-control': 'no-cache'
|
||||||
body: json.encode(body));
|
},
|
||||||
|
body: json.encode(body),
|
||||||
|
);
|
||||||
|
|
||||||
if (response.statusCode != 200) {
|
if (response.statusCode != 200) {
|
||||||
throw BuyException(title: providerDescription, content: 'Url $url is not found!');
|
throw BuyException(title: providerDescription, content: 'Url $url is not found!');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||||
final urlFromResponse = responseJSON['url'] as String;
|
final urlFromResponse = responseJSON['url'] as String;
|
||||||
return urlFromResponse;
|
return urlFromResponse;
|
||||||
|
@ -101,18 +104,21 @@ class WyreBuyProvider extends BuyProvider {
|
||||||
'country': _countryCode
|
'country': _countryCode
|
||||||
};
|
};
|
||||||
final uri = Uri.parse(quoteUrl);
|
final uri = Uri.parse(quoteUrl);
|
||||||
final response = await post(uri,
|
final response = await ProxyWrapper().post(
|
||||||
headers: {
|
clearnetUri: uri,
|
||||||
'Authorization': 'Bearer $_secretKey',
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Authorization': 'Bearer $_secretKey',
|
||||||
'cache-control': 'no-cache'
|
'Content-Type': 'application/json',
|
||||||
},
|
'cache-control': 'no-cache'
|
||||||
body: json.encode(body));
|
},
|
||||||
|
body: json.encode(body),
|
||||||
|
);
|
||||||
|
|
||||||
if (response.statusCode != 200) {
|
if (response.statusCode != 200) {
|
||||||
throw BuyException(title: providerDescription, content: 'Quote is not found!');
|
throw BuyException(title: providerDescription, content: 'Quote is not found!');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||||
final sourceAmount = responseJSON['sourceAmount'] as double;
|
final sourceAmount = responseJSON['sourceAmount'] as double;
|
||||||
final destAmount = responseJSON['destAmount'] as double;
|
final destAmount = responseJSON['destAmount'] as double;
|
||||||
|
@ -125,8 +131,7 @@ class WyreBuyProvider extends BuyProvider {
|
||||||
Future<Order> findOrderById(String id) async {
|
Future<Order> findOrderById(String id) async {
|
||||||
final orderUrl = baseApiUrl + _ordersSuffix + '/$id';
|
final orderUrl = baseApiUrl + _ordersSuffix + '/$id';
|
||||||
final orderUri = Uri.parse(orderUrl);
|
final orderUri = Uri.parse(orderUrl);
|
||||||
final orderResponse = await get(orderUri);
|
final orderResponse = await ProxyWrapper().get(clearnetUri: orderUri);
|
||||||
|
|
||||||
if (orderResponse.statusCode != 200) {
|
if (orderResponse.statusCode != 200) {
|
||||||
throw BuyException(title: providerDescription, content: 'Order $id is not found!');
|
throw BuyException(title: providerDescription, content: 'Order $id is not found!');
|
||||||
}
|
}
|
||||||
|
@ -142,8 +147,7 @@ class WyreBuyProvider extends BuyProvider {
|
||||||
|
|
||||||
final transferUrl = baseApiUrl + _transferSuffix + transferId + _trackSuffix;
|
final transferUrl = baseApiUrl + _transferSuffix + transferId + _trackSuffix;
|
||||||
final transferUri = Uri.parse(transferUrl);
|
final transferUri = Uri.parse(transferUrl);
|
||||||
final transferResponse = await get(transferUri);
|
final transferResponse = await ProxyWrapper().get(clearnetUri: transferUri);
|
||||||
|
|
||||||
if (transferResponse.statusCode != 200) {
|
if (transferResponse.statusCode != 200) {
|
||||||
throw BuyException(title: providerDescription, content: 'Transfer $transferId is not found!');
|
throw BuyException(title: providerDescription, content: 'Transfer $transferId is not found!');
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,9 +3,9 @@ import 'dart:convert';
|
||||||
import 'package:cake_wallet/cake_pay/cake_pay_order.dart';
|
import 'package:cake_wallet/cake_pay/cake_pay_order.dart';
|
||||||
import 'package:cake_wallet/cake_pay/cake_pay_user_credentials.dart';
|
import 'package:cake_wallet/cake_pay/cake_pay_user_credentials.dart';
|
||||||
import 'package:cake_wallet/cake_pay/cake_pay_vendor.dart';
|
import 'package:cake_wallet/cake_pay/cake_pay_vendor.dart';
|
||||||
|
import 'package:cw_core/utils/proxy_wrapper.dart';
|
||||||
import 'package:cw_core/utils/print_verbose.dart';
|
import 'package:cw_core/utils/print_verbose.dart';
|
||||||
import 'package:cake_wallet/entities/country.dart';
|
import 'package:cake_wallet/entities/country.dart';
|
||||||
import 'package:http/http.dart' as http;
|
|
||||||
|
|
||||||
class CakePayApi {
|
class CakePayApi {
|
||||||
static const testBaseUri = false;
|
static const testBaseUri = false;
|
||||||
|
@ -32,12 +32,17 @@ class CakePayApi {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
'Authorization': 'Api-Key $apiKey',
|
'Authorization': 'Api-Key $apiKey',
|
||||||
};
|
};
|
||||||
final response = await http.post(uri, headers: headers, body: json.encode({'email': email}));
|
final response = await ProxyWrapper().post(
|
||||||
|
clearnetUri: uri,
|
||||||
|
headers: headers,
|
||||||
|
body: json.encode({'email': email}),
|
||||||
|
);
|
||||||
|
|
||||||
if (response.statusCode != 200) {
|
if (response.statusCode != 200) {
|
||||||
throw Exception('Unexpected http status: ${response.statusCode}');
|
throw Exception('Unexpected http status: ${response.statusCode}');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
final bodyJson = json.decode(response.body) as Map<String, dynamic>;
|
final bodyJson = json.decode(response.body) as Map<String, dynamic>;
|
||||||
|
|
||||||
if (bodyJson.containsKey('user') && bodyJson['user']['email'] != null) {
|
if (bodyJson.containsKey('user') && bodyJson['user']['email'] != null) {
|
||||||
|
@ -64,12 +69,17 @@ class CakePayApi {
|
||||||
};
|
};
|
||||||
final query = <String, String>{'email': email, 'otp': code};
|
final query = <String, String>{'email': email, 'otp': code};
|
||||||
|
|
||||||
final response = await http.post(uri, headers: headers, body: json.encode(query));
|
final response = await ProxyWrapper().post(
|
||||||
|
clearnetUri: uri,
|
||||||
|
headers: headers,
|
||||||
|
body: json.encode(query),
|
||||||
|
);
|
||||||
|
|
||||||
if (response.statusCode != 200) {
|
if (response.statusCode != 200) {
|
||||||
throw Exception('Unexpected http status: ${response.statusCode}');
|
throw Exception('Unexpected http status: ${response.statusCode}');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
final bodyJson = json.decode(response.body) as Map<String, dynamic>;
|
final bodyJson = json.decode(response.body) as Map<String, dynamic>;
|
||||||
|
|
||||||
if (bodyJson.containsKey('error')) {
|
if (bodyJson.containsKey('error')) {
|
||||||
|
@ -116,9 +126,14 @@ class CakePayApi {
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final response = await http.post(uri, headers: headers, body: json.encode(query));
|
final response = await ProxyWrapper().post(
|
||||||
|
clearnetUri: uri,
|
||||||
|
headers: headers,
|
||||||
|
body: json.encode(query),
|
||||||
|
);
|
||||||
|
|
||||||
if (response.statusCode != 201) {
|
if (response.statusCode != 201) {
|
||||||
|
|
||||||
final responseBody = json.decode(response.body);
|
final responseBody = json.decode(response.body);
|
||||||
if (responseBody is List) {
|
if (responseBody is List) {
|
||||||
throw '${responseBody[0]}';
|
throw '${responseBody[0]}';
|
||||||
|
@ -127,6 +142,7 @@ class CakePayApi {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
final bodyJson = json.decode(response.body) as Map<String, dynamic>;
|
final bodyJson = json.decode(response.body) as Map<String, dynamic>;
|
||||||
return CakePayOrder.fromMap(bodyJson);
|
return CakePayOrder.fromMap(bodyJson);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -145,7 +161,8 @@ class CakePayApi {
|
||||||
'X-CSRFToken': CSRFToken,
|
'X-CSRFToken': CSRFToken,
|
||||||
};
|
};
|
||||||
|
|
||||||
final response = await http.get(uri, headers: headers);
|
final response = await ProxyWrapper().get(clearnetUri: uri, headers: headers);
|
||||||
|
|
||||||
|
|
||||||
printV('Response: ${response.statusCode}');
|
printV('Response: ${response.statusCode}');
|
||||||
|
|
||||||
|
@ -168,7 +185,11 @@ class CakePayApi {
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final response = await http.post(uri, headers: headers, body: json.encode({'email': email}));
|
final response = await ProxyWrapper().post(
|
||||||
|
clearnetUri: uri,
|
||||||
|
headers: headers,
|
||||||
|
body: json.encode({'email': email}),
|
||||||
|
);
|
||||||
|
|
||||||
if (response.statusCode != 200) {
|
if (response.statusCode != 200) {
|
||||||
throw Exception('Unexpected http status: ${response.statusCode}');
|
throw Exception('Unexpected http status: ${response.statusCode}');
|
||||||
|
@ -187,8 +208,8 @@ class CakePayApi {
|
||||||
'Authorization': 'Api-Key $apiKey',
|
'Authorization': 'Api-Key $apiKey',
|
||||||
};
|
};
|
||||||
|
|
||||||
final response = await http.get(uri, headers: headers);
|
final response = await ProxyWrapper().get(clearnetUri: uri, headers: headers);
|
||||||
|
|
||||||
if (response.statusCode != 200) {
|
if (response.statusCode != 200) {
|
||||||
throw Exception('Unexpected http status: ${response.statusCode}');
|
throw Exception('Unexpected http status: ${response.statusCode}');
|
||||||
}
|
}
|
||||||
|
@ -234,14 +255,15 @@ class CakePayApi {
|
||||||
'Authorization': 'Api-Key $apiKey',
|
'Authorization': 'Api-Key $apiKey',
|
||||||
};
|
};
|
||||||
|
|
||||||
var response = await http.get(uri, headers: headers);
|
var response = await ProxyWrapper().get(clearnetUri: uri, headers: headers);
|
||||||
|
|
||||||
|
|
||||||
if (response.statusCode != 200) {
|
if (response.statusCode != 200) {
|
||||||
throw Exception(
|
throw Exception(
|
||||||
'Failed to fetch vendors: statusCode - ${response.statusCode}, queryParams -$queryParams, response - ${response.body}');
|
'Failed to fetch vendors: statusCode - ${response.statusCode}, queryParams -$queryParams, response - ${response.body}');
|
||||||
}
|
}
|
||||||
|
|
||||||
final bodyJson = json.decode(utf8.decode(response.bodyBytes));
|
final bodyJson = json.decode(response.body);
|
||||||
|
|
||||||
if (bodyJson is List<dynamic> && bodyJson.isEmpty) {
|
if (bodyJson is List<dynamic> && bodyJson.isEmpty) {
|
||||||
return [];
|
return [];
|
||||||
|
|
|
@ -38,9 +38,9 @@ class AddressValidator extends TextValidator {
|
||||||
'|[0-9a-zA-Z]{105}|addr1[0-9a-zA-Z]{98}';
|
'|[0-9a-zA-Z]{105}|addr1[0-9a-zA-Z]{98}';
|
||||||
case CryptoCurrency.btc:
|
case CryptoCurrency.btc:
|
||||||
pattern =
|
pattern =
|
||||||
'${P2pkhAddress.regex.pattern}|${P2shAddress.regex.pattern}|${RegExp(r'(bc|tb)1q[ac-hj-np-z02-9]{25,39}}').pattern}|${P2trAddress.regex.pattern}|${P2wshAddress.regex.pattern}|${SilentPaymentAddress.regex.pattern}';
|
'${P2pkhAddress.regex.pattern}|${P2shAddress.regex.pattern}|${P2wpkhAddress.regex.pattern}|${P2trAddress.regex.pattern}|${P2wshAddress.regex.pattern}|${SilentPaymentAddress.regex.pattern}';
|
||||||
case CryptoCurrency.ltc:
|
case CryptoCurrency.ltc:
|
||||||
pattern = '^${RegExp(r'ltc1q[ac-hj-np-z02-9]{25,39}').pattern}\$|^${MwebAddress.regex.pattern}\$';
|
pattern = '${P2wpkhAddress.regex.pattern}|${MwebAddress.regex.pattern}';
|
||||||
case CryptoCurrency.nano:
|
case CryptoCurrency.nano:
|
||||||
pattern = '[0-9a-zA-Z_]+';
|
pattern = '[0-9a-zA-Z_]+';
|
||||||
case CryptoCurrency.banano:
|
case CryptoCurrency.banano:
|
||||||
|
@ -335,10 +335,6 @@ class AddressValidator extends TextValidator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pattern != null) {
|
return pattern != null ? "($pattern)" : null;
|
||||||
return "$BEFORE_REGEX($pattern)$AFTER_REGEX";
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import 'package:cake_wallet/di.dart';
|
||||||
import 'package:cake_wallet/entities/preferences_key.dart';
|
import 'package:cake_wallet/entities/preferences_key.dart';
|
||||||
import 'package:cake_wallet/store/settings_store.dart';
|
import 'package:cake_wallet/store/settings_store.dart';
|
||||||
import 'package:cake_wallet/utils/feature_flag.dart';
|
import 'package:cake_wallet/utils/feature_flag.dart';
|
||||||
|
import 'package:cake_wallet/utils/tor.dart';
|
||||||
import 'package:cake_wallet/view_model/wallet_list/wallet_list_item.dart';
|
import 'package:cake_wallet/view_model/wallet_list/wallet_list_item.dart';
|
||||||
import 'package:cake_wallet/view_model/wallet_list/wallet_list_view_model.dart';
|
import 'package:cake_wallet/view_model/wallet_list/wallet_list_view_model.dart';
|
||||||
import 'package:cw_core/sync_status.dart';
|
import 'package:cw_core/sync_status.dart';
|
||||||
|
@ -15,6 +16,7 @@ import 'package:cw_core/utils/print_verbose.dart';
|
||||||
import 'package:cw_core/wallet_type.dart';
|
import 'package:cw_core/wallet_type.dart';
|
||||||
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
|
||||||
class BackgroundSync {
|
class BackgroundSync {
|
||||||
final FlutterLocalNotificationsPlugin _notificationsPlugin = FlutterLocalNotificationsPlugin();
|
final FlutterLocalNotificationsPlugin _notificationsPlugin = FlutterLocalNotificationsPlugin();
|
||||||
|
@ -90,6 +92,11 @@ class BackgroundSync {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> sync() async {
|
Future<void> sync() async {
|
||||||
|
final settingsStore = getIt.get<SettingsStore>();
|
||||||
|
if (settingsStore.currentBuiltinTor) {
|
||||||
|
printV("Starting Tor");
|
||||||
|
await ensureTorStarted(context: null);
|
||||||
|
}
|
||||||
printV("Background sync started");
|
printV("Background sync started");
|
||||||
await _syncWallets();
|
await _syncWallets();
|
||||||
printV("Background sync completed");
|
printV("Background sync completed");
|
||||||
|
@ -100,7 +107,6 @@ class BackgroundSync {
|
||||||
final walletListViewModel = getIt.get<WalletListViewModel>();
|
final walletListViewModel = getIt.get<WalletListViewModel>();
|
||||||
final settingsStore = getIt.get<SettingsStore>();
|
final settingsStore = getIt.get<SettingsStore>();
|
||||||
|
|
||||||
|
|
||||||
final List<WalletListItem> moneroWallets = walletListViewModel.wallets
|
final List<WalletListItem> moneroWallets = walletListViewModel.wallets
|
||||||
.where((element) => !element.isHardware)
|
.where((element) => !element.isHardware)
|
||||||
.where((element) => ![WalletType.haven, WalletType.decred].contains(element.type))
|
.where((element) => ![WalletType.haven, WalletType.decred].contains(element.type))
|
||||||
|
|
|
@ -110,7 +110,7 @@ class $BackupService {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> verifyWallets() async {
|
Future<void> verifyWallets() async {
|
||||||
final walletInfoSource = await _reloadHiveWalletInfoBox();
|
final walletInfoSource = await reloadHiveWalletInfoBox();
|
||||||
correctWallets =
|
correctWallets =
|
||||||
walletInfoSource.values.where((info) => availableWalletTypes.contains(info.type)).toList();
|
walletInfoSource.values.where((info) => availableWalletTypes.contains(info.type)).toList();
|
||||||
|
|
||||||
|
@ -119,7 +119,7 @@ class $BackupService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Box<WalletInfo>> _reloadHiveWalletInfoBox() async {
|
Future<Box<WalletInfo>> reloadHiveWalletInfoBox() async {
|
||||||
final appDir = await getAppDir();
|
final appDir = await getAppDir();
|
||||||
await CakeHive.close();
|
await CakeHive.close();
|
||||||
CakeHive.init(appDir.path);
|
CakeHive.init(appDir.path);
|
||||||
|
@ -288,13 +288,15 @@ class $BackupService {
|
||||||
return {
|
return {
|
||||||
'name': walletInfo.name,
|
'name': walletInfo.name,
|
||||||
'type': walletInfo.type.toString(),
|
'type': walletInfo.type.toString(),
|
||||||
'password': await keyService.getWalletPassword(walletName: walletInfo.name)
|
'password': await keyService.getWalletPassword(walletName: walletInfo.name),
|
||||||
|
'hardwareWalletType': walletInfo.hardwareWalletType?.index,
|
||||||
};
|
};
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return {
|
return {
|
||||||
'name': walletInfo.name,
|
'name': walletInfo.name,
|
||||||
'type': walletInfo.type.toString(),
|
'type': walletInfo.type.toString(),
|
||||||
'password': ''
|
'password': '',
|
||||||
|
'hardwareWalletType': walletInfo.hardwareWalletType?.index,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
|
@ -10,6 +10,7 @@ import 'package:cake_wallet/utils/package_info.dart';
|
||||||
import 'package:crypto/crypto.dart';
|
import 'package:crypto/crypto.dart';
|
||||||
import 'package:cw_core/root_dir.dart';
|
import 'package:cw_core/root_dir.dart';
|
||||||
import 'package:cw_core/utils/print_verbose.dart';
|
import 'package:cw_core/utils/print_verbose.dart';
|
||||||
|
import 'package:cw_core/wallet_info.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
|
|
||||||
enum BackupVersion {
|
enum BackupVersion {
|
||||||
|
@ -305,6 +306,7 @@ class BackupServiceV3 extends $BackupService {
|
||||||
|
|
||||||
// Continue importing the backup the old way
|
// Continue importing the backup the old way
|
||||||
await super.verifyWallets();
|
await super.verifyWallets();
|
||||||
|
await verifyHardwareWallets(password);
|
||||||
await super.importKeychainDumpV2(password);
|
await super.importKeychainDumpV2(password);
|
||||||
await super.importPreferencesDump();
|
await super.importPreferencesDump();
|
||||||
await super.importTransactionDescriptionDump();
|
await super.importTransactionDescriptionDump();
|
||||||
|
@ -313,6 +315,39 @@ class BackupServiceV3 extends $BackupService {
|
||||||
decryptedData.deleteSync();
|
decryptedData.deleteSync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> verifyHardwareWallets(String password,
|
||||||
|
{String keychainSalt = secrets.backupKeychainSalt}) async {
|
||||||
|
final walletInfoSource = await reloadHiveWalletInfoBox();
|
||||||
|
final appDir = await getAppDir();
|
||||||
|
final keychainDumpFile = File('${appDir.path}/~_keychain_dump');
|
||||||
|
final decryptedKeychainDumpFileData = await decryptV2(
|
||||||
|
keychainDumpFile.readAsBytesSync(), '$keychainSalt$password');
|
||||||
|
final keychainJSON = json.decode(utf8.decode(decryptedKeychainDumpFileData))
|
||||||
|
as Map<String, dynamic>;
|
||||||
|
final keychainWalletsInfo = keychainJSON['wallets'] as List;
|
||||||
|
|
||||||
|
final expectedHardwareWallets = keychainWalletsInfo
|
||||||
|
.where((e) =>
|
||||||
|
(e as Map<String, dynamic>).containsKey("hardwareWalletType") &&
|
||||||
|
e["hardwareWalletType"] != null)
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
for (final expectedHardwareWallet in expectedHardwareWallets) {
|
||||||
|
final info = expectedHardwareWallet as Map<String, dynamic>;
|
||||||
|
final actualWalletInfo = walletInfoSource.values
|
||||||
|
.where((e) =>
|
||||||
|
e.name == info['name'] && e.type.toString() == info['type'])
|
||||||
|
.firstOrNull;
|
||||||
|
if (actualWalletInfo != null &&
|
||||||
|
info["hardwareWalletType"] !=
|
||||||
|
actualWalletInfo.hardwareWalletType?.index) {
|
||||||
|
actualWalletInfo.hardwareWalletType =
|
||||||
|
HardwareWalletType.values[info["hardwareWalletType"] as int];
|
||||||
|
await actualWalletInfo.save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Future<File> exportBackupFileV3(String password, {String nonce = secrets.backupSalt}) async {
|
Future<File> exportBackupFileV3(String password, {String nonce = secrets.backupSalt}) async {
|
||||||
final metadata = BackupMetadata(
|
final metadata = BackupMetadata(
|
||||||
version: BackupVersion.v3,
|
version: BackupVersion.v3,
|
||||||
|
@ -467,4 +502,4 @@ This backup was created on ${DateTime.now().toIso8601String()}
|
||||||
file.writeAsBytesSync(data);
|
file.writeAsBytesSync(data);
|
||||||
return file;
|
return file;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,18 +1,15 @@
|
||||||
|
import 'package:cw_core/utils/proxy_wrapper.dart';
|
||||||
import 'package:cw_core/crypto_currency.dart';
|
import 'package:cw_core/crypto_currency.dart';
|
||||||
import 'package:cake_wallet/entities/fiat_currency.dart';
|
import 'package:cake_wallet/entities/fiat_currency.dart';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'package:flutter/foundation.dart';
|
|
||||||
import 'package:http/http.dart';
|
|
||||||
import 'package:cake_wallet/.secrets.g.dart' as secrets;
|
import 'package:cake_wallet/.secrets.g.dart' as secrets;
|
||||||
|
|
||||||
const _fiatApiClearNetAuthority = 'fiat-api.cakewallet.com';
|
const _fiatApiClearNetAuthority = 'fiat-api.cakewallet.com';
|
||||||
const _fiatApiOnionAuthority = 'n4z7bdcmwk2oyddxvzaap3x2peqcplh3pzdy7tpkk5ejz5n4mhfvoxqd.onion';
|
// const _fiatApiOnionAuthority = 'n4z7bdcmwk2oyddxvzaap3x2peqcplh3pzdy7tpkk5ejz5n4mhfvoxqd.onion';
|
||||||
|
const _fiatApiOnionAuthority = _fiatApiClearNetAuthority;
|
||||||
const _fiatApiPath = '/v2/rates';
|
const _fiatApiPath = '/v2/rates';
|
||||||
|
|
||||||
Future<double> _fetchPrice(Map<String, dynamic> args) async {
|
Future<double> _fetchPrice(String crypto, String fiat, bool torOnly) async {
|
||||||
final crypto = args['crypto'] as String;
|
|
||||||
final fiat = args['fiat'] as String;
|
|
||||||
final torOnly = args['torOnly'] as bool;
|
|
||||||
|
|
||||||
final Map<String, String> queryParams = {
|
final Map<String, String> queryParams = {
|
||||||
'interval_count': '1',
|
'interval_count': '1',
|
||||||
|
@ -24,14 +21,14 @@ Future<double> _fetchPrice(Map<String, dynamic> args) async {
|
||||||
num price = 0.0;
|
num price = 0.0;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
late final Uri uri;
|
final onionUri = Uri.http(_fiatApiOnionAuthority, _fiatApiPath, queryParams);
|
||||||
if (torOnly) {
|
final clearnetUri = Uri.https(_fiatApiClearNetAuthority, _fiatApiPath, queryParams);
|
||||||
uri = Uri.http(_fiatApiOnionAuthority, _fiatApiPath, queryParams);
|
|
||||||
} else {
|
|
||||||
uri = Uri.https(_fiatApiClearNetAuthority, _fiatApiPath, queryParams);
|
|
||||||
}
|
|
||||||
|
|
||||||
final response = await get(uri);
|
final response = await ProxyWrapper().get(
|
||||||
|
onionUri: onionUri,
|
||||||
|
clearnetUri: torOnly ? onionUri : clearnetUri,
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
if (response.statusCode != 200) {
|
if (response.statusCode != 200) {
|
||||||
return 0.0;
|
return 0.0;
|
||||||
|
@ -50,18 +47,11 @@ Future<double> _fetchPrice(Map<String, dynamic> args) async {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<double> _fetchPriceAsync(CryptoCurrency crypto, FiatCurrency fiat, bool torOnly) async =>
|
|
||||||
compute(_fetchPrice, {
|
|
||||||
'fiat': fiat.toString(),
|
|
||||||
'crypto': crypto.toString(),
|
|
||||||
'torOnly': torOnly,
|
|
||||||
});
|
|
||||||
|
|
||||||
class FiatConversionService {
|
class FiatConversionService {
|
||||||
static Future<double> fetchPrice({
|
static Future<double> fetchPrice({
|
||||||
required CryptoCurrency crypto,
|
required CryptoCurrency crypto,
|
||||||
required FiatCurrency fiat,
|
required FiatCurrency fiat,
|
||||||
required bool torOnly,
|
required bool torOnly,
|
||||||
}) async =>
|
}) async =>
|
||||||
await _fetchPriceAsync(crypto, fiat, torOnly);
|
await _fetchPrice(crypto.toString(), fiat.toString(), torOnly);
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,15 +5,13 @@ import 'package:cake_wallet/core/open_crypto_pay/exceptions.dart';
|
||||||
import 'package:cake_wallet/core/open_crypto_pay/lnurl.dart';
|
import 'package:cake_wallet/core/open_crypto_pay/lnurl.dart';
|
||||||
import 'package:cake_wallet/core/open_crypto_pay/models.dart';
|
import 'package:cake_wallet/core/open_crypto_pay/models.dart';
|
||||||
import 'package:cw_core/crypto_currency.dart';
|
import 'package:cw_core/crypto_currency.dart';
|
||||||
import 'package:http/http.dart';
|
import 'package:cw_core/utils/proxy_wrapper.dart';
|
||||||
|
|
||||||
class OpenCryptoPayService {
|
class OpenCryptoPayService {
|
||||||
static bool isOpenCryptoPayQR(String value) =>
|
static bool isOpenCryptoPayQR(String value) =>
|
||||||
value.toLowerCase().contains("lightning=lnurl") ||
|
value.toLowerCase().contains("lightning=lnurl") ||
|
||||||
value.toLowerCase().startsWith("lnurl");
|
value.toLowerCase().startsWith("lnurl");
|
||||||
|
|
||||||
final Client _httpClient = Client();
|
|
||||||
|
|
||||||
Future<String> commitOpenCryptoPayRequest(
|
Future<String> commitOpenCryptoPayRequest(
|
||||||
String txHex, {
|
String txHex, {
|
||||||
required String txId,
|
required String txId,
|
||||||
|
@ -31,7 +29,8 @@ class OpenCryptoPayService {
|
||||||
queryParams['tx'] = txId;
|
queryParams['tx'] = txId;
|
||||||
|
|
||||||
final response =
|
final response =
|
||||||
await _httpClient.get(Uri.https(uri.authority, uri.path, queryParams));
|
await ProxyWrapper().get(clearnetUri: Uri.https(uri.authority, uri.path, queryParams));
|
||||||
|
|
||||||
|
|
||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
final body = jsonDecode(response.body) as Map;
|
final body = jsonDecode(response.body) as Map;
|
||||||
|
@ -40,13 +39,13 @@ class OpenCryptoPayService {
|
||||||
throw OpenCryptoPayException(body.toString());
|
throw OpenCryptoPayException(body.toString());
|
||||||
}
|
}
|
||||||
throw OpenCryptoPayException(
|
throw OpenCryptoPayException(
|
||||||
"Unexpected status code ${response.statusCode} ${response.body}");
|
"Unexpected status code ${response.statusCode} ${response}");
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> cancelOpenCryptoPayRequest(OpenCryptoPayRequest request) async {
|
Future<void> cancelOpenCryptoPayRequest(OpenCryptoPayRequest request) async {
|
||||||
final uri = Uri.parse(request.callbackUrl.replaceAll("/cb/", "/cancel/"));
|
final uri = Uri.parse(request.callbackUrl.replaceAll("/cb/", "/cancel/"));
|
||||||
|
|
||||||
await _httpClient.delete(uri);
|
await ProxyWrapper().delete(clearnetUri: uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<OpenCryptoPayRequest> getOpenCryptoPayInvoice(String lnUrl) async {
|
Future<OpenCryptoPayRequest> getOpenCryptoPayInvoice(String lnUrl) async {
|
||||||
|
@ -73,7 +72,8 @@ class OpenCryptoPayService {
|
||||||
|
|
||||||
Future<(_OpenCryptoPayQuote, Map<String, List<OpenCryptoPayQuoteAsset>>)>
|
Future<(_OpenCryptoPayQuote, Map<String, List<OpenCryptoPayQuoteAsset>>)>
|
||||||
_getOpenCryptoPayParams(Uri uri) async {
|
_getOpenCryptoPayParams(Uri uri) async {
|
||||||
final response = await _httpClient.get(uri);
|
final response = await ProxyWrapper().get(clearnetUri: uri);
|
||||||
|
|
||||||
|
|
||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
final responseBody = jsonDecode(response.body) as Map;
|
final responseBody = jsonDecode(response.body) as Map;
|
||||||
|
@ -119,8 +119,8 @@ class OpenCryptoPayService {
|
||||||
queryParams['asset'] = asset.title;
|
queryParams['asset'] = asset.title;
|
||||||
queryParams['method'] = _getMethod(asset);
|
queryParams['method'] = _getMethod(asset);
|
||||||
|
|
||||||
final response =
|
final response = await ProxyWrapper().get(clearnetUri: Uri.https(uri.authority, uri.path, queryParams));
|
||||||
await _httpClient.get(Uri.https(uri.authority, uri.path, queryParams));
|
|
||||||
|
|
||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
final responseBody = jsonDecode(response.body) as Map;
|
final responseBody = jsonDecode(response.body) as Map;
|
||||||
|
|
|
@ -73,8 +73,11 @@ class WalletLoadingService {
|
||||||
return wallet;
|
return wallet;
|
||||||
} catch (error, stack) {
|
} catch (error, stack) {
|
||||||
await ExceptionHandler.resetLastPopupDate();
|
await ExceptionHandler.resetLastPopupDate();
|
||||||
|
final isLedgerError = await ExceptionHandler.isLedgerError(error);
|
||||||
|
if (isLedgerError) rethrow;
|
||||||
await ExceptionHandler.onError(FlutterErrorDetails(exception: error, stack: stack));
|
await ExceptionHandler.onError(FlutterErrorDetails(exception: error, stack: stack));
|
||||||
|
|
||||||
|
|
||||||
// try fetching the seeds of the corrupted wallet to show it to the user
|
// try fetching the seeds of the corrupted wallet to show it to the user
|
||||||
String corruptedWalletsSeeds = "Corrupted wallets seeds (if retrievable, empty otherwise):";
|
String corruptedWalletsSeeds = "Corrupted wallets seeds (if retrievable, empty otherwise):";
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:cake_wallet/entities/yat_record.dart';
|
import 'package:cake_wallet/entities/yat_record.dart';
|
||||||
import 'package:http/http.dart';
|
import 'package:cw_core/utils/proxy_wrapper.dart';
|
||||||
|
|
||||||
class YatService {
|
class YatService {
|
||||||
static bool isDevMode = false;
|
static bool isDevMode = false;
|
||||||
|
@ -33,7 +33,8 @@ class YatService {
|
||||||
final yatRecords = <YatRecord>[];
|
final yatRecords = <YatRecord>[];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final response = await get(uri);
|
final response = await ProxyWrapper().get(clearnetUri: uri);
|
||||||
|
|
||||||
final resBody = json.decode(response.body) as Map<String, dynamic>;
|
final resBody = json.decode(response.body) as Map<String, dynamic>;
|
||||||
final results = resBody["result"] as Map<dynamic, dynamic>;
|
final results = resBody["result"] as Map<dynamic, dynamic>;
|
||||||
// Favour a subaddress over a standard address.
|
// Favour a subaddress over a standard address.
|
||||||
|
@ -42,7 +43,7 @@ class YatService {
|
||||||
results[MONERO_STD_ADDRESS] ??
|
results[MONERO_STD_ADDRESS] ??
|
||||||
results[tag]) as Map<String, dynamic>;
|
results[tag]) as Map<String, dynamic>;
|
||||||
|
|
||||||
if (yatRecord != null) {
|
if (yatRecord.isNotEmpty) {
|
||||||
yatRecords.add(YatRecord.fromJson(yatRecord));
|
yatRecords.add(YatRecord.fromJson(yatRecord));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
17
lib/di.dart
17
lib/di.dart
|
@ -41,9 +41,12 @@ import 'package:cake_wallet/src/screens/address_book/supported_handles_page.dart
|
||||||
import 'package:cake_wallet/src/screens/address_book/contact_list_page.dart';
|
import 'package:cake_wallet/src/screens/address_book/contact_list_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/dev/monero_background_sync.dart';
|
import 'package:cake_wallet/src/screens/dev/monero_background_sync.dart';
|
||||||
import 'package:cake_wallet/src/screens/dev/moneroc_call_profiler.dart';
|
import 'package:cake_wallet/src/screens/dev/moneroc_call_profiler.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/dev/network_requests.dart';
|
||||||
import 'package:cake_wallet/src/screens/dev/secure_preferences_page.dart';
|
import 'package:cake_wallet/src/screens/dev/secure_preferences_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/dev/shared_preferences_page.dart';
|
import 'package:cake_wallet/src/screens/dev/shared_preferences_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/integrations/deuro/savings_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/settings/background_sync_page.dart';
|
import 'package:cake_wallet/src/screens/settings/background_sync_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/start_tor/start_tor_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/wallet_connect/services/bottom_sheet_service.dart';
|
import 'package:cake_wallet/src/screens/wallet_connect/services/bottom_sheet_service.dart';
|
||||||
import 'package:cake_wallet/src/screens/wallet_connect/services/key_service/wallet_connect_key_service.dart';
|
import 'package:cake_wallet/src/screens/wallet_connect/services/key_service/wallet_connect_key_service.dart';
|
||||||
import 'package:cake_wallet/src/screens/wallet_connect/services/walletkit_service.dart';
|
import 'package:cake_wallet/src/screens/wallet_connect/services/walletkit_service.dart';
|
||||||
|
@ -51,9 +54,11 @@ import 'package:cake_wallet/themes/core/theme_store.dart';
|
||||||
import 'package:cake_wallet/view_model/dev/monero_background_sync.dart';
|
import 'package:cake_wallet/view_model/dev/monero_background_sync.dart';
|
||||||
import 'package:cake_wallet/view_model/dev/secure_preferences.dart';
|
import 'package:cake_wallet/view_model/dev/secure_preferences.dart';
|
||||||
import 'package:cake_wallet/view_model/dev/shared_preferences.dart';
|
import 'package:cake_wallet/view_model/dev/shared_preferences.dart';
|
||||||
|
import 'package:cake_wallet/view_model/integrations/deuro_view_model.dart';
|
||||||
import 'package:cake_wallet/view_model/link_view_model.dart';
|
import 'package:cake_wallet/view_model/link_view_model.dart';
|
||||||
import 'package:cake_wallet/tron/tron.dart';
|
import 'package:cake_wallet/tron/tron.dart';
|
||||||
import 'package:cake_wallet/src/screens/transaction_details/rbf_details_page.dart';
|
import 'package:cake_wallet/src/screens/transaction_details/rbf_details_page.dart';
|
||||||
|
import 'package:cake_wallet/view_model/start_tor_view_model.dart';
|
||||||
import 'package:cw_core/receive_page_option.dart';
|
import 'package:cw_core/receive_page_option.dart';
|
||||||
import 'package:cake_wallet/entities/wallet_edit_page_arguments.dart';
|
import 'package:cake_wallet/entities/wallet_edit_page_arguments.dart';
|
||||||
import 'package:cake_wallet/entities/wallet_manager.dart';
|
import 'package:cake_wallet/entities/wallet_manager.dart';
|
||||||
|
@ -142,7 +147,6 @@ import 'package:cake_wallet/src/screens/settings/other_settings_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/settings/privacy_page.dart';
|
import 'package:cake_wallet/src/screens/settings/privacy_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/settings/security_backup_page.dart';
|
import 'package:cake_wallet/src/screens/settings/security_backup_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/settings/silent_payments_settings.dart';
|
import 'package:cake_wallet/src/screens/settings/silent_payments_settings.dart';
|
||||||
import 'package:cake_wallet/src/screens/settings/tor_page.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/settings/trocador_providers_page.dart';
|
import 'package:cake_wallet/src/screens/settings/trocador_providers_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/setup_2fa/modify_2fa_page.dart';
|
import 'package:cake_wallet/src/screens/setup_2fa/modify_2fa_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa.dart';
|
import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa.dart';
|
||||||
|
@ -694,7 +698,6 @@ Future<void> setup({
|
||||||
return walletKitService;
|
return walletKitService;
|
||||||
});
|
});
|
||||||
|
|
||||||
getIt.registerFactory(() => NFTViewModel(appStore, getIt.get<BottomSheetService>()));
|
|
||||||
getIt.registerFactory(() => BalancePage(
|
getIt.registerFactory(() => BalancePage(
|
||||||
nftViewModel: getIt.get<NFTViewModel>(),
|
nftViewModel: getIt.get<NFTViewModel>(),
|
||||||
dashboardViewModel: getIt.get<DashboardViewModel>(),
|
dashboardViewModel: getIt.get<DashboardViewModel>(),
|
||||||
|
@ -1545,7 +1548,7 @@ Future<void> setup({
|
||||||
() => WalletConnectConnectionsView(walletKitService: getIt.get<WalletKitService>()),
|
() => WalletConnectConnectionsView(walletKitService: getIt.get<WalletKitService>()),
|
||||||
);
|
);
|
||||||
|
|
||||||
getIt.registerFactory<TorPage>(() => TorPage(getIt.get<AppStore>()));
|
getIt.registerFactory(() => NFTViewModel(appStore, getIt.get<BottomSheetService>()));
|
||||||
|
|
||||||
getIt.registerFactory(() => SignViewModel(getIt.get<AppStore>().wallet!));
|
getIt.registerFactory(() => SignViewModel(getIt.get<AppStore>().wallet!));
|
||||||
|
|
||||||
|
@ -1562,6 +1565,14 @@ Future<void> setup({
|
||||||
getIt.registerFactory(() => BackgroundSyncLogsViewModel());
|
getIt.registerFactory(() => BackgroundSyncLogsViewModel());
|
||||||
|
|
||||||
getIt.registerFactory(() => DevBackgroundSyncLogsPage(getIt.get<BackgroundSyncLogsViewModel>()));
|
getIt.registerFactory(() => DevBackgroundSyncLogsPage(getIt.get<BackgroundSyncLogsViewModel>()));
|
||||||
|
|
||||||
|
getIt.registerFactory(() => DevNetworkRequests());
|
||||||
|
|
||||||
|
getIt.registerFactory(() => StartTorPage(StartTorViewModel(),));
|
||||||
|
|
||||||
|
getIt.registerFactory(() => DEuroViewModel(getIt<AppStore>()));
|
||||||
|
|
||||||
|
getIt.registerFactory(() => DEuroSavingsPage(getIt<DEuroViewModel>()));
|
||||||
|
|
||||||
_isSetupFinished = true;
|
_isSetupFinished = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,16 @@
|
||||||
import 'package:cake_wallet/ethereum/ethereum.dart';
|
import 'package:cake_wallet/ethereum/ethereum.dart';
|
||||||
import 'package:cake_wallet/polygon/polygon.dart';
|
import 'package:cake_wallet/polygon/polygon.dart';
|
||||||
|
import 'package:cw_core/utils/proxy_wrapper.dart';
|
||||||
import 'package:cw_core/utils/print_verbose.dart';
|
import 'package:cw_core/utils/print_verbose.dart';
|
||||||
import 'package:cw_core/wallet_base.dart';
|
import 'package:cw_core/wallet_base.dart';
|
||||||
import 'package:cw_core/wallet_type.dart';
|
import 'package:cw_core/wallet_type.dart';
|
||||||
import 'package:ens_dart/ens_dart.dart';
|
import 'package:ens_dart/ens_dart.dart';
|
||||||
import 'package:http/http.dart';
|
|
||||||
import 'package:web3dart/web3dart.dart';
|
import 'package:web3dart/web3dart.dart';
|
||||||
|
|
||||||
class EnsRecord {
|
class EnsRecord {
|
||||||
|
|
||||||
static Future<String> fetchEnsAddress(String name, {WalletBase? wallet}) async {
|
static Future<String> fetchEnsAddress(String name, {WalletBase? wallet}) async {
|
||||||
|
|
||||||
Web3Client? _client;
|
Web3Client? _client;
|
||||||
|
|
||||||
if (wallet != null && wallet.type == WalletType.ethereum) {
|
if (wallet != null && wallet.type == WalletType.ethereum) {
|
||||||
|
@ -20,7 +22,9 @@ class EnsRecord {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_client == null) {
|
if (_client == null) {
|
||||||
_client = Web3Client("https://ethereum-rpc.publicnode.com", Client());
|
late final client = ProxyWrapper().getHttpIOClient();
|
||||||
|
|
||||||
|
_client = Web3Client("https://ethereum-rpc.publicnode.com", client);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:cw_core/utils/proxy_wrapper.dart';
|
||||||
|
|
||||||
class FioAddressProvider {
|
class FioAddressProvider {
|
||||||
static const apiAuthority = 'fio.blockpane.com';
|
static const apiAuthority = 'fio.blockpane.com';
|
||||||
|
@ -13,13 +13,17 @@ class FioAddressProvider {
|
||||||
final body = <String, String>{"fio_name": fioAddress};
|
final body = <String, String>{"fio_name": fioAddress};
|
||||||
|
|
||||||
final uri = Uri.https(apiAuthority, availCheck);
|
final uri = Uri.https(apiAuthority, availCheck);
|
||||||
final response =
|
final response = await ProxyWrapper().post(
|
||||||
await http.post(uri, headers: headers, body: json.encode(body));
|
clearnetUri: uri,
|
||||||
|
headers: headers,
|
||||||
|
body: json.encode(body),
|
||||||
|
);
|
||||||
|
|
||||||
if (response.statusCode != 200) {
|
if (response.statusCode != 200) {
|
||||||
return isFioRegistered;
|
return isFioRegistered;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||||
isFioRegistered = responseJSON['is_registered'] as int == 1;
|
isFioRegistered = responseJSON['is_registered'] as int == 1;
|
||||||
|
|
||||||
|
@ -35,9 +39,13 @@ class FioAddressProvider {
|
||||||
};
|
};
|
||||||
|
|
||||||
final uri = Uri.https(apiAuthority, getAddress);
|
final uri = Uri.https(apiAuthority, getAddress);
|
||||||
final response =
|
final response = await ProxyWrapper().post(
|
||||||
await http.post(uri, headers: headers, body: json.encode(body));
|
clearnetUri: uri,
|
||||||
|
headers: headers,
|
||||||
|
body: json.encode(body),
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
if (response.statusCode == 400) {
|
if (response.statusCode == 400) {
|
||||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||||
final error = responseJSON['error'] as String;
|
final error = responseJSON['error'] as String;
|
||||||
|
|
|
@ -66,6 +66,7 @@ class PreferencesKey {
|
||||||
static const moneroWalletPasswordUpdateV1Base = 'monero_wallet_update_v1';
|
static const moneroWalletPasswordUpdateV1Base = 'monero_wallet_update_v1';
|
||||||
static const syncModeKey = 'sync_mode';
|
static const syncModeKey = 'sync_mode';
|
||||||
static const syncAllKey = 'sync_all';
|
static const syncAllKey = 'sync_all';
|
||||||
|
static const builtinTorKey = 'builtin_tor';
|
||||||
static const lastPopupDate = 'last_popup_date';
|
static const lastPopupDate = 'last_popup_date';
|
||||||
static const lastAppReviewDate = 'last_app_review_date';
|
static const lastAppReviewDate = 'last_app_review_date';
|
||||||
static const sortBalanceBy = 'sort_balance_by';
|
static const sortBalanceBy = 'sort_balance_by';
|
||||||
|
|
|
@ -5,7 +5,7 @@ part 'transaction_description.g.dart';
|
||||||
|
|
||||||
@HiveType(typeId: TransactionDescription.typeId)
|
@HiveType(typeId: TransactionDescription.typeId)
|
||||||
class TransactionDescription extends HiveObject {
|
class TransactionDescription extends HiveObject {
|
||||||
TransactionDescription({required this.id, this.recipientAddress, this.transactionNote});
|
TransactionDescription({required this.id, this.recipientAddress, this.transactionNote, this.transactionKey});
|
||||||
|
|
||||||
static const typeId = TRANSACTION_TYPE_ID;
|
static const typeId = TRANSACTION_TYPE_ID;
|
||||||
static const boxName = 'TransactionDescriptions';
|
static const boxName = 'TransactionDescriptions';
|
||||||
|
@ -20,12 +20,16 @@ class TransactionDescription extends HiveObject {
|
||||||
@HiveField(2)
|
@HiveField(2)
|
||||||
String? transactionNote;
|
String? transactionNote;
|
||||||
|
|
||||||
|
@HiveField(3)
|
||||||
|
String? transactionKey;
|
||||||
|
|
||||||
String get note => transactionNote ?? '';
|
String get note => transactionNote ?? '';
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => {
|
Map<String, dynamic> toJson() => {
|
||||||
'id': id,
|
'id': id,
|
||||||
'recipientAddress': recipientAddress,
|
'recipientAddress': recipientAddress,
|
||||||
'transactionNote': transactionNote,
|
'transactionNote': transactionNote,
|
||||||
|
'transactionKey': transactionKey,
|
||||||
};
|
};
|
||||||
|
|
||||||
factory TransactionDescription.fromJson(Map<String, dynamic> json) {
|
factory TransactionDescription.fromJson(Map<String, dynamic> json) {
|
||||||
|
@ -33,6 +37,7 @@ class TransactionDescription extends HiveObject {
|
||||||
id: json['id'] as String,
|
id: json['id'] as String,
|
||||||
recipientAddress: json['recipientAddress'] as String?,
|
recipientAddress: json['recipientAddress'] as String?,
|
||||||
transactionNote: json['transactionNote'] as String?,
|
transactionNote: json['transactionNote'] as String?,
|
||||||
|
transactionKey: json['transactionKey'] as String?,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,16 @@
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:cw_core/utils/print_verbose.dart';
|
import 'package:cw_core/utils/print_verbose.dart';
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:cw_core/utils/proxy_wrapper.dart';
|
||||||
|
|
||||||
Future<String> fetchUnstoppableDomainAddress(String domain, String ticker) async {
|
Future<String> fetchUnstoppableDomainAddress(String domain, String ticker) async {
|
||||||
var address = '';
|
var address = '';
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final uri = Uri.parse("https://api.unstoppabledomains.com/profile/public/${Uri.encodeQueryComponent(domain)}?fields=records");
|
final uri = Uri.parse("https://api.unstoppabledomains.com/profile/public/${Uri.encodeQueryComponent(domain)}?fields=records");
|
||||||
final jsonString = await http.read(uri);
|
final response = await ProxyWrapper().get(clearnetUri: uri);
|
||||||
final jsonParsed = json.decode(jsonString) as Map<String, dynamic>;
|
|
||||||
|
final jsonParsed = json.decode(response.body) as Map<String, dynamic>;
|
||||||
if (jsonParsed["records"] == null) {
|
if (jsonParsed["records"] == null) {
|
||||||
throw Exception(".records response from $uri is empty");
|
throw Exception(".records response from $uri is empty");
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,7 +2,7 @@ import 'dart:convert';
|
||||||
|
|
||||||
import 'package:cw_core/crypto_currency.dart';
|
import 'package:cw_core/crypto_currency.dart';
|
||||||
import 'package:cw_core/utils/print_verbose.dart';
|
import 'package:cw_core/utils/print_verbose.dart';
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:cw_core/utils/proxy_wrapper.dart';
|
||||||
|
|
||||||
class WellKnownRecord {
|
class WellKnownRecord {
|
||||||
WellKnownRecord({
|
WellKnownRecord({
|
||||||
|
@ -40,14 +40,15 @@ class WellKnownRecord {
|
||||||
}
|
}
|
||||||
|
|
||||||
// lookup domain/.well-known/nano-currency.json and check if it has a nano address:
|
// lookup domain/.well-known/nano-currency.json and check if it has a nano address:
|
||||||
final http.Response response = await http.get(
|
final response = await ProxyWrapper().get(
|
||||||
Uri.parse("https://$domain/.well-known/$jsonLocation.json?names=$name"),
|
clearnetUri: Uri.parse("https://$domain/.well-known/$jsonLocation.json?names=$name"),
|
||||||
headers: <String, String>{"Accept": "application/json"},
|
headers: <String, String>{"Accept": "application/json"},
|
||||||
);
|
);
|
||||||
|
|
||||||
if (response.statusCode != 200) {
|
if (response.statusCode != 200) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
final Map<String, dynamic> decoded = json.decode(response.body) as Map<String, dynamic>;
|
final Map<String, dynamic> decoded = json.decode(response.body) as Map<String, dynamic>;
|
||||||
|
|
||||||
// Access the first element in the names array and retrieve its address
|
// Access the first element in the names array and retrieve its address
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:cw_core/utils/print_verbose.dart';
|
import 'package:cw_core/utils/print_verbose.dart';
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:cw_core/utils/proxy_wrapper.dart';
|
||||||
|
|
||||||
class ZanoAlias {
|
class ZanoAlias {
|
||||||
static Future<String?> fetchZanoAliasAddress(String alias) async {
|
static Future<String?> fetchZanoAliasAddress(String alias) async {
|
||||||
try {
|
try {
|
||||||
final uri = Uri.parse("http://zano.cakewallet.com:11211/json_rpc");
|
final uri = Uri.parse("http://zano.cakewallet.com:11211/json_rpc");
|
||||||
final response = await http.post(
|
final response = await ProxyWrapper().post(
|
||||||
uri,
|
clearnetUri: uri,
|
||||||
body: json.encode({
|
body: json.encode({
|
||||||
"id": 0,
|
"id": 0,
|
||||||
"jsonrpc": "2.0",
|
"jsonrpc": "2.0",
|
||||||
|
@ -16,6 +16,7 @@ class ZanoAlias {
|
||||||
"params": {"alias": alias}
|
"params": {"alias": alias}
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
final jsonParsed = json.decode(response.body) as Map<String, dynamic>;
|
final jsonParsed = json.decode(response.body) as Map<String, dynamic>;
|
||||||
|
|
||||||
return jsonParsed['result']['alias_details']['address'] as String?;
|
return jsonParsed['result']['alias_details']['address'] as String?;
|
||||||
|
|
|
@ -67,8 +67,7 @@ class CWEthereum extends Ethereum {
|
||||||
@override
|
@override
|
||||||
String getPublicKey(WalletBase wallet) {
|
String getPublicKey(WalletBase wallet) {
|
||||||
final privateKeyInUnitInt = (wallet as EthereumWallet).evmChainPrivateKey;
|
final privateKeyInUnitInt = (wallet as EthereumWallet).evmChainPrivateKey;
|
||||||
final publicKey = privateKeyInUnitInt.address.hex;
|
return privateKeyInUnitInt.address.hex;
|
||||||
return publicKey;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -138,29 +137,24 @@ class CWEthereum extends Ethereum {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Erc20Token> getERC20Currencies(WalletBase wallet) {
|
List<Erc20Token> getERC20Currencies(WalletBase wallet) =>
|
||||||
final ethereumWallet = wallet as EthereumWallet;
|
(wallet as EthereumWallet).erc20Currencies;
|
||||||
return ethereumWallet.erc20Currencies;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> addErc20Token(WalletBase wallet, CryptoCurrency token) async {
|
Future<void> addErc20Token(WalletBase wallet, CryptoCurrency token) =>
|
||||||
await (wallet as EthereumWallet).addErc20Token(token as Erc20Token);
|
(wallet as EthereumWallet).addErc20Token(token as Erc20Token);
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> deleteErc20Token(WalletBase wallet, CryptoCurrency token) async =>
|
Future<void> deleteErc20Token(WalletBase wallet, CryptoCurrency token) =>
|
||||||
await (wallet as EthereumWallet).deleteErc20Token(token as Erc20Token);
|
(wallet as EthereumWallet).deleteErc20Token(token as Erc20Token);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> removeTokenTransactionsInHistory(WalletBase wallet, CryptoCurrency token) async =>
|
Future<void> removeTokenTransactionsInHistory(WalletBase wallet, CryptoCurrency token) =>
|
||||||
await (wallet as EthereumWallet).removeTokenTransactionsInHistory(token as Erc20Token);
|
(wallet as EthereumWallet).removeTokenTransactionsInHistory(token as Erc20Token);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Erc20Token?> getErc20Token(WalletBase wallet, String contractAddress) async {
|
Future<Erc20Token?> getErc20Token(WalletBase wallet, String contractAddress) =>
|
||||||
final ethereumWallet = wallet as EthereumWallet;
|
(wallet as EthereumWallet).getErc20Token(contractAddress, 'eth');
|
||||||
return await ethereumWallet.getErc20Token(contractAddress, 'eth');
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
CryptoCurrency assetOfTransaction(WalletBase wallet, TransactionInfo transaction) {
|
CryptoCurrency assetOfTransaction(WalletBase wallet, TransactionInfo transaction) {
|
||||||
|
@ -177,23 +171,19 @@ class CWEthereum extends Ethereum {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void updateEtherscanUsageState(WalletBase wallet, bool isEnabled) {
|
void updateEtherscanUsageState(WalletBase wallet, bool isEnabled) =>
|
||||||
(wallet as EthereumWallet).updateScanProviderUsageState(isEnabled);
|
(wallet as EthereumWallet).updateScanProviderUsageState(isEnabled);
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Web3Client? getWeb3Client(WalletBase wallet) {
|
Web3Client? getWeb3Client(WalletBase wallet) => (wallet as EthereumWallet).getWeb3Client();
|
||||||
return (wallet as EthereumWallet).getWeb3Client();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
@override
|
||||||
String getTokenAddress(CryptoCurrency asset) => (asset as Erc20Token).contractAddress;
|
String getTokenAddress(CryptoCurrency asset) => (asset as Erc20Token).contractAddress;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void setLedgerConnection(
|
void setLedgerConnection(WalletBase wallet, ledger.LedgerConnection connection) {
|
||||||
WalletBase wallet, ledger.LedgerConnection connection) {
|
|
||||||
((wallet as EVMChainWallet).evmChainPrivateKey as EvmLedgerCredentials)
|
((wallet as EVMChainWallet).evmChainPrivateKey as EvmLedgerCredentials)
|
||||||
.setLedgerConnection(
|
.setLedgerConnection(connection, wallet.walletInfo.derivationInfo?.derivationPath);
|
||||||
connection, wallet.walletInfo.derivationInfo?.derivationPath);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -209,7 +199,44 @@ class CWEthereum extends Ethereum {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<String> getDefaultTokenContractAddresses() {
|
List<String> getDefaultTokenContractAddresses() =>
|
||||||
return DefaultEthereumErc20Tokens().initialErc20Tokens.map((e) => e.contractAddress).toList();
|
DefaultEthereumErc20Tokens().initialErc20Tokens.map((e) => e.contractAddress).toList();
|
||||||
}
|
|
||||||
|
Future<PendingTransaction> createTokenApproval(WalletBase wallet, BigInt amount, String spender,
|
||||||
|
CryptoCurrency token, TransactionPriority priority) =>
|
||||||
|
(wallet as EVMChainWallet).createApprovalTransaction(
|
||||||
|
amount, spender, token, priority as EVMChainTransactionPriority);
|
||||||
|
|
||||||
|
// Integrations
|
||||||
|
@override
|
||||||
|
Future<BigInt> getDEuroSavingsBalance(WalletBase wallet) =>
|
||||||
|
DEuro(wallet as EthereumWallet).savingsBalance;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<BigInt> getDEuroAccruedInterest(WalletBase wallet) =>
|
||||||
|
DEuro(wallet as EthereumWallet).accruedInterest;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<BigInt> getDEuroInterestRate(WalletBase wallet) =>
|
||||||
|
DEuro(wallet as EthereumWallet).interestRate;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<BigInt> getDEuroSavingsApproved(WalletBase wallet) =>
|
||||||
|
DEuro(wallet as EthereumWallet).approvedBalance;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<PendingTransaction> addDEuroSaving(
|
||||||
|
WalletBase wallet, BigInt amount, TransactionPriority priority) =>
|
||||||
|
DEuro(wallet as EthereumWallet)
|
||||||
|
.depositSavings(amount, priority as EVMChainTransactionPriority);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<PendingTransaction> removeDEuroSaving(
|
||||||
|
WalletBase wallet, BigInt amount, TransactionPriority priority) =>
|
||||||
|
DEuro(wallet as EthereumWallet)
|
||||||
|
.withdrawSavings(amount, priority as EVMChainTransactionPriority);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<PendingTransaction> enableDEuroSaving(WalletBase wallet, TransactionPriority priority) =>
|
||||||
|
DEuro(wallet as EthereumWallet).enableSavings(priority as EVMChainTransactionPriority);
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@ import 'package:cake_wallet/exchange/utils/currency_pairs_utils.dart';
|
||||||
import 'package:cw_core/crypto_currency.dart';
|
import 'package:cw_core/crypto_currency.dart';
|
||||||
import 'package:cw_core/utils/print_verbose.dart';
|
import 'package:cw_core/utils/print_verbose.dart';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:cw_core/utils/proxy_wrapper.dart';
|
||||||
|
|
||||||
class ChainflipExchangeProvider extends ExchangeProvider {
|
class ChainflipExchangeProvider extends ExchangeProvider {
|
||||||
ChainflipExchangeProvider({required this.tradesStore})
|
ChainflipExchangeProvider({required this.tradesStore})
|
||||||
|
@ -275,7 +275,8 @@ class ChainflipExchangeProvider extends ExchangeProvider {
|
||||||
Future<Map<String, dynamic>> _getRequest(String path, Map<String, String> params) async {
|
Future<Map<String, dynamic>> _getRequest(String path, Map<String, String> params) async {
|
||||||
final uri = Uri.https(_baseURL, path, params);
|
final uri = Uri.https(_baseURL, path, params);
|
||||||
|
|
||||||
final response = await http.get(uri);
|
final response = await ProxyWrapper().get(clearnetUri: uri);
|
||||||
|
|
||||||
|
|
||||||
if ((response.statusCode != 200) || (response.body.contains('error'))) {
|
if ((response.statusCode != 200) || (response.body.contains('error'))) {
|
||||||
throw Exception('Unexpected response: ${response.statusCode} / ${uri.toString()} / ${response.body}');
|
throw Exception('Unexpected response: ${response.statusCode} / ${uri.toString()} / ${response.body}');
|
||||||
|
@ -287,7 +288,8 @@ class ChainflipExchangeProvider extends ExchangeProvider {
|
||||||
Future<Map<String, dynamic>?> _getStatus(Map<String, String> params) async {
|
Future<Map<String, dynamic>?> _getStatus(Map<String, String> params) async {
|
||||||
final uri = Uri.https(_baseURL, _txInfoPath, params);
|
final uri = Uri.https(_baseURL, _txInfoPath, params);
|
||||||
|
|
||||||
final response = await http.get(uri);
|
final response = await ProxyWrapper().get(clearnetUri: uri);
|
||||||
|
|
||||||
|
|
||||||
if (response.statusCode == 404) return null;
|
if (response.statusCode == 404) return null;
|
||||||
|
|
||||||
|
|
|
@ -13,11 +13,11 @@ import 'package:cake_wallet/exchange/utils/currency_pairs_utils.dart';
|
||||||
import 'package:cake_wallet/store/settings_store.dart';
|
import 'package:cake_wallet/store/settings_store.dart';
|
||||||
import 'package:cake_wallet/utils/device_info.dart';
|
import 'package:cake_wallet/utils/device_info.dart';
|
||||||
import 'package:cake_wallet/utils/distribution_info.dart';
|
import 'package:cake_wallet/utils/distribution_info.dart';
|
||||||
|
import 'package:cw_core/utils/proxy_wrapper.dart';
|
||||||
import 'package:cake_wallet/wallet_type_utils.dart';
|
import 'package:cake_wallet/wallet_type_utils.dart';
|
||||||
import 'package:cw_core/crypto_currency.dart';
|
import 'package:cw_core/crypto_currency.dart';
|
||||||
import 'package:cw_core/utils/print_verbose.dart';
|
import 'package:cw_core/utils/print_verbose.dart';
|
||||||
import 'package:http/http.dart';
|
import 'package:cw_core/utils/proxy_wrapper.dart';
|
||||||
|
|
||||||
class ChangeNowExchangeProvider extends ExchangeProvider {
|
class ChangeNowExchangeProvider extends ExchangeProvider {
|
||||||
ChangeNowExchangeProvider({required SettingsStore settingsStore})
|
ChangeNowExchangeProvider({required SettingsStore settingsStore})
|
||||||
: _settingsStore = settingsStore,
|
: _settingsStore = settingsStore,
|
||||||
|
@ -73,7 +73,8 @@ class ChangeNowExchangeProvider extends ExchangeProvider {
|
||||||
'flow': _getFlow(isFixedRateMode)
|
'flow': _getFlow(isFixedRateMode)
|
||||||
};
|
};
|
||||||
final uri = Uri.https(apiAuthority, rangePath, params);
|
final uri = Uri.https(apiAuthority, rangePath, params);
|
||||||
final response = await get(uri, headers: headers);
|
final response = await ProxyWrapper().get(clearnetUri: uri, headers: headers);
|
||||||
|
|
||||||
|
|
||||||
if (response.statusCode == 400) {
|
if (response.statusCode == 400) {
|
||||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||||
|
@ -118,7 +119,8 @@ class ChangeNowExchangeProvider extends ExchangeProvider {
|
||||||
params['fromAmount'] = amount.toString();
|
params['fromAmount'] = amount.toString();
|
||||||
|
|
||||||
final uri = Uri.https(apiAuthority, estimatedAmountPath, params);
|
final uri = Uri.https(apiAuthority, estimatedAmountPath, params);
|
||||||
final response = await get(uri, headers: headers);
|
final response = await ProxyWrapper().get(clearnetUri: uri, headers: headers);
|
||||||
|
|
||||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||||
final fromAmount = double.parse(responseJSON['fromAmount'].toString());
|
final fromAmount = double.parse(responseJSON['fromAmount'].toString());
|
||||||
final toAmount = double.parse(responseJSON['toAmount'].toString());
|
final toAmount = double.parse(responseJSON['toAmount'].toString());
|
||||||
|
@ -177,7 +179,12 @@ class ChangeNowExchangeProvider extends ExchangeProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
final uri = Uri.https(apiAuthority, createTradePath);
|
final uri = Uri.https(apiAuthority, createTradePath);
|
||||||
final response = await post(uri, headers: headers, body: json.encode(body));
|
final response = await ProxyWrapper().post(
|
||||||
|
clearnetUri: uri,
|
||||||
|
headers: headers,
|
||||||
|
body: json.encode(body),
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
if (response.statusCode == 400) {
|
if (response.statusCode == 400) {
|
||||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||||
|
@ -220,7 +227,8 @@ class ChangeNowExchangeProvider extends ExchangeProvider {
|
||||||
final headers = {apiHeaderKey: apiKey};
|
final headers = {apiHeaderKey: apiKey};
|
||||||
final params = <String, String>{'id': id};
|
final params = <String, String>{'id': id};
|
||||||
final uri = Uri.https(apiAuthority, findTradeByIdPath, params);
|
final uri = Uri.https(apiAuthority, findTradeByIdPath, params);
|
||||||
final response = await get(uri, headers: headers);
|
final response = await ProxyWrapper().get(clearnetUri: uri, headers: headers);
|
||||||
|
|
||||||
|
|
||||||
if (response.statusCode == 404) throw TradeNotFoundException(id, provider: description);
|
if (response.statusCode == 404) throw TradeNotFoundException(id, provider: description);
|
||||||
|
|
||||||
|
|
|
@ -9,9 +9,9 @@ import 'package:cake_wallet/exchange/trade_not_found_exception.dart';
|
||||||
import 'package:cake_wallet/exchange/trade_request.dart';
|
import 'package:cake_wallet/exchange/trade_request.dart';
|
||||||
import 'package:cake_wallet/exchange/trade_state.dart';
|
import 'package:cake_wallet/exchange/trade_state.dart';
|
||||||
import 'package:cake_wallet/exchange/utils/currency_pairs_utils.dart';
|
import 'package:cake_wallet/exchange/utils/currency_pairs_utils.dart';
|
||||||
|
import 'package:cw_core/utils/proxy_wrapper.dart';
|
||||||
import 'package:cw_core/crypto_currency.dart';
|
import 'package:cw_core/crypto_currency.dart';
|
||||||
import 'package:cw_core/utils/print_verbose.dart';
|
import 'package:cw_core/utils/print_verbose.dart';
|
||||||
import 'package:http/http.dart';
|
|
||||||
|
|
||||||
class ExolixExchangeProvider extends ExchangeProvider {
|
class ExolixExchangeProvider extends ExchangeProvider {
|
||||||
ExolixExchangeProvider() : super(pairList: supportedPairs(_notSupported));
|
ExolixExchangeProvider() : super(pairList: supportedPairs(_notSupported));
|
||||||
|
@ -86,8 +86,9 @@ class ExolixExchangeProvider extends ExchangeProvider {
|
||||||
// Maximum of 2 attempts to fetch limits
|
// Maximum of 2 attempts to fetch limits
|
||||||
for (int i = 0; i < 2; i++) {
|
for (int i = 0; i < 2; i++) {
|
||||||
final uri = Uri.https(apiBaseUrl, ratePath, params);
|
final uri = Uri.https(apiBaseUrl, ratePath, params);
|
||||||
final response = await get(uri);
|
final response = await ProxyWrapper().get(clearnetUri: uri);
|
||||||
|
|
||||||
|
|
||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||||
final minAmount = responseJSON['minAmount'];
|
final minAmount = responseJSON['minAmount'];
|
||||||
|
@ -133,7 +134,8 @@ class ExolixExchangeProvider extends ExchangeProvider {
|
||||||
params['amount'] = amount.toString();
|
params['amount'] = amount.toString();
|
||||||
|
|
||||||
final uri = Uri.https(apiBaseUrl, ratePath, params);
|
final uri = Uri.https(apiBaseUrl, ratePath, params);
|
||||||
final response = await get(uri);
|
final response = await ProxyWrapper().get(clearnetUri: uri);
|
||||||
|
|
||||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||||
|
|
||||||
if (response.statusCode != 200) {
|
if (response.statusCode != 200) {
|
||||||
|
@ -172,7 +174,12 @@ class ExolixExchangeProvider extends ExchangeProvider {
|
||||||
body['amount'] = request.fromAmount;
|
body['amount'] = request.fromAmount;
|
||||||
|
|
||||||
final uri = Uri.https(apiBaseUrl, transactionsPath);
|
final uri = Uri.https(apiBaseUrl, transactionsPath);
|
||||||
final response = await post(uri, headers: headers, body: json.encode(body));
|
final response = await ProxyWrapper().post(
|
||||||
|
clearnetUri: uri,
|
||||||
|
headers: headers,
|
||||||
|
body: json.encode(body),
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
if (response.statusCode == 400) {
|
if (response.statusCode == 400) {
|
||||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||||
|
@ -214,8 +221,8 @@ class ExolixExchangeProvider extends ExchangeProvider {
|
||||||
Future<Trade> findTradeById({required String id}) async {
|
Future<Trade> findTradeById({required String id}) async {
|
||||||
final findTradeByIdPath = '$transactionsPath/$id';
|
final findTradeByIdPath = '$transactionsPath/$id';
|
||||||
final uri = Uri.https(apiBaseUrl, findTradeByIdPath);
|
final uri = Uri.https(apiBaseUrl, findTradeByIdPath);
|
||||||
final response = await get(uri);
|
final response = await ProxyWrapper().get(clearnetUri: uri);
|
||||||
|
|
||||||
if (response.statusCode == 404) throw TradeNotFoundException(id, provider: description);
|
if (response.statusCode == 404) throw TradeNotFoundException(id, provider: description);
|
||||||
|
|
||||||
if (response.statusCode == 400) {
|
if (response.statusCode == 400) {
|
||||||
|
|
|
@ -10,9 +10,9 @@ import 'package:cake_wallet/exchange/trade_not_created_exception.dart';
|
||||||
import 'package:cake_wallet/exchange/trade_request.dart';
|
import 'package:cake_wallet/exchange/trade_request.dart';
|
||||||
import 'package:cake_wallet/exchange/trade_state.dart';
|
import 'package:cake_wallet/exchange/trade_state.dart';
|
||||||
import 'package:cake_wallet/exchange/utils/currency_pairs_utils.dart';
|
import 'package:cake_wallet/exchange/utils/currency_pairs_utils.dart';
|
||||||
|
import 'package:cw_core/utils/proxy_wrapper.dart';
|
||||||
import 'package:cw_core/crypto_currency.dart';
|
import 'package:cw_core/crypto_currency.dart';
|
||||||
import 'package:cw_core/utils/print_verbose.dart';
|
import 'package:cw_core/utils/print_verbose.dart';
|
||||||
import 'package:http/http.dart' as http;
|
|
||||||
|
|
||||||
class LetsExchangeExchangeProvider extends ExchangeProvider {
|
class LetsExchangeExchangeProvider extends ExchangeProvider {
|
||||||
LetsExchangeExchangeProvider() : super(pairList: supportedPairs(_notSupported));
|
LetsExchangeExchangeProvider() : super(pairList: supportedPairs(_notSupported));
|
||||||
|
@ -152,7 +152,11 @@ class LetsExchangeExchangeProvider extends ExchangeProvider {
|
||||||
|
|
||||||
final uri = Uri.https(_baseUrl,
|
final uri = Uri.https(_baseUrl,
|
||||||
isFixedRateMode ? _createTransactionRevertPath : _createTransactionPath, tradeParams);
|
isFixedRateMode ? _createTransactionRevertPath : _createTransactionPath, tradeParams);
|
||||||
final response = await http.post(uri, headers: headers);
|
final response = await ProxyWrapper().post(
|
||||||
|
clearnetUri: uri,
|
||||||
|
headers: headers,
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
if (response.statusCode != 200) {
|
if (response.statusCode != 200) {
|
||||||
throw Exception('LetsExchange create trade failed: ${response.body}');
|
throw Exception('LetsExchange create trade failed: ${response.body}');
|
||||||
|
@ -218,7 +222,8 @@ class LetsExchangeExchangeProvider extends ExchangeProvider {
|
||||||
};
|
};
|
||||||
|
|
||||||
final url = Uri.https(_baseUrl, '$_getTransactionPath/$id');
|
final url = Uri.https(_baseUrl, '$_getTransactionPath/$id');
|
||||||
final response = await http.get(url, headers: headers);
|
final response = await ProxyWrapper().get(clearnetUri: url, headers: headers);
|
||||||
|
|
||||||
|
|
||||||
if (response.statusCode != 200) {
|
if (response.statusCode != 200) {
|
||||||
throw Exception('LetsExchange fetch trade failed: ${response.body}');
|
throw Exception('LetsExchange fetch trade failed: ${response.body}');
|
||||||
|
@ -266,7 +271,11 @@ class LetsExchangeExchangeProvider extends ExchangeProvider {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final uri = Uri.https(_baseUrl, isFixedRateMode ? _infoRevertPath : _infoPath, params);
|
final uri = Uri.https(_baseUrl, isFixedRateMode ? _infoRevertPath : _infoPath, params);
|
||||||
final response = await http.post(uri, headers: headers);
|
final response = await ProxyWrapper().post(
|
||||||
|
clearnetUri: uri,
|
||||||
|
headers: headers,
|
||||||
|
);
|
||||||
|
|
||||||
if (response.statusCode != 200) {
|
if (response.statusCode != 200) {
|
||||||
throw Exception('LetsExchange fetch info failed: ${response.body}');
|
throw Exception('LetsExchange fetch info failed: ${response.body}');
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:developer';
|
|
||||||
|
|
||||||
import 'package:cake_wallet/.secrets.g.dart' as secrets;
|
import 'package:cake_wallet/.secrets.g.dart' as secrets;
|
||||||
import 'package:cake_wallet/exchange/provider/exchange_provider.dart';
|
import 'package:cake_wallet/exchange/provider/exchange_provider.dart';
|
||||||
|
@ -11,9 +10,9 @@ import 'package:cake_wallet/exchange/trade_not_found_exception.dart';
|
||||||
import 'package:cake_wallet/exchange/trade_request.dart';
|
import 'package:cake_wallet/exchange/trade_request.dart';
|
||||||
import 'package:cake_wallet/exchange/trade_state.dart';
|
import 'package:cake_wallet/exchange/trade_state.dart';
|
||||||
import 'package:cake_wallet/exchange/utils/currency_pairs_utils.dart';
|
import 'package:cake_wallet/exchange/utils/currency_pairs_utils.dart';
|
||||||
|
import 'package:cw_core/utils/proxy_wrapper.dart';
|
||||||
import 'package:cw_core/crypto_currency.dart';
|
import 'package:cw_core/crypto_currency.dart';
|
||||||
import 'package:cw_core/utils/print_verbose.dart';
|
import 'package:cw_core/utils/print_verbose.dart';
|
||||||
import 'package:http/http.dart';
|
|
||||||
|
|
||||||
class SideShiftExchangeProvider extends ExchangeProvider {
|
class SideShiftExchangeProvider extends ExchangeProvider {
|
||||||
SideShiftExchangeProvider() : super(pairList: supportedPairs(_notSupported));
|
SideShiftExchangeProvider() : super(pairList: supportedPairs(_notSupported));
|
||||||
|
@ -60,8 +59,8 @@ class SideShiftExchangeProvider extends ExchangeProvider {
|
||||||
Future<bool> checkIsAvailable() async {
|
Future<bool> checkIsAvailable() async {
|
||||||
const url = apiBaseUrl + permissionPath;
|
const url = apiBaseUrl + permissionPath;
|
||||||
final uri = Uri.parse(url);
|
final uri = Uri.parse(url);
|
||||||
final response = await get(uri);
|
final response = await ProxyWrapper().get(clearnetUri: uri);
|
||||||
|
|
||||||
if (response.statusCode == 500) {
|
if (response.statusCode == 500) {
|
||||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||||
final error = responseJSON['error']['message'] as String;
|
final error = responseJSON['error']['message'] as String;
|
||||||
|
@ -90,7 +89,8 @@ class SideShiftExchangeProvider extends ExchangeProvider {
|
||||||
"$apiBaseUrl$rangePath/${fromCurrency.title.toLowerCase()}-$fromNetwork/${toCurrency.title.toLowerCase()}-$toNetwork";
|
"$apiBaseUrl$rangePath/${fromCurrency.title.toLowerCase()}-$fromNetwork/${toCurrency.title.toLowerCase()}-$toNetwork";
|
||||||
|
|
||||||
final uri = Uri.parse(url);
|
final uri = Uri.parse(url);
|
||||||
final response = await get(uri);
|
final response = await ProxyWrapper().get(clearnetUri: uri);
|
||||||
|
|
||||||
|
|
||||||
if (response.statusCode == 500) {
|
if (response.statusCode == 500) {
|
||||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||||
|
@ -137,7 +137,8 @@ class SideShiftExchangeProvider extends ExchangeProvider {
|
||||||
"$apiBaseUrl$rangePath/$fromCurrency-$depositNetwork/$toCurrency-$settleNetwork?amount=$amount";
|
"$apiBaseUrl$rangePath/$fromCurrency-$depositNetwork/$toCurrency-$settleNetwork?amount=$amount";
|
||||||
|
|
||||||
final uri = Uri.parse(url);
|
final uri = Uri.parse(url);
|
||||||
final response = await get(uri);
|
final response = await ProxyWrapper().get(clearnetUri: uri);
|
||||||
|
|
||||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||||
|
|
||||||
if (response.statusCode == 500) {
|
if (response.statusCode == 500) {
|
||||||
|
@ -186,7 +187,12 @@ class SideShiftExchangeProvider extends ExchangeProvider {
|
||||||
final headers = {'Content-Type': 'application/json'};
|
final headers = {'Content-Type': 'application/json'};
|
||||||
|
|
||||||
final uri = Uri.parse(url);
|
final uri = Uri.parse(url);
|
||||||
final response = await post(uri, headers: headers, body: json.encode(body));
|
final response = await ProxyWrapper().post(
|
||||||
|
clearnetUri: uri,
|
||||||
|
headers: headers,
|
||||||
|
body: json.encode(body),
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
if (response.statusCode != 201) {
|
if (response.statusCode != 201) {
|
||||||
if (response.statusCode == 400) {
|
if (response.statusCode == 400) {
|
||||||
|
@ -227,8 +233,8 @@ class SideShiftExchangeProvider extends ExchangeProvider {
|
||||||
Future<Trade> findTradeById({required String id}) async {
|
Future<Trade> findTradeById({required String id}) async {
|
||||||
final url = apiBaseUrl + orderPath + '/' + id;
|
final url = apiBaseUrl + orderPath + '/' + id;
|
||||||
final uri = Uri.parse(url);
|
final uri = Uri.parse(url);
|
||||||
final response = await get(uri);
|
final response = await ProxyWrapper().get(clearnetUri: uri);
|
||||||
|
|
||||||
if (response.statusCode == 404) {
|
if (response.statusCode == 404) {
|
||||||
throw TradeNotFoundException(id, provider: description);
|
throw TradeNotFoundException(id, provider: description);
|
||||||
}
|
}
|
||||||
|
@ -281,7 +287,12 @@ class SideShiftExchangeProvider extends ExchangeProvider {
|
||||||
'depositNetwork': _networkFor(request.fromCurrency),
|
'depositNetwork': _networkFor(request.fromCurrency),
|
||||||
};
|
};
|
||||||
final uri = Uri.parse(url);
|
final uri = Uri.parse(url);
|
||||||
final response = await post(uri, headers: headers, body: json.encode(body));
|
final response = await ProxyWrapper().post(
|
||||||
|
clearnetUri: uri,
|
||||||
|
headers: headers,
|
||||||
|
body: json.encode(body),
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
if (response.statusCode != 201) {
|
if (response.statusCode != 201) {
|
||||||
if (response.statusCode == 400) {
|
if (response.statusCode == 400) {
|
||||||
|
|
|
@ -11,8 +11,8 @@ import 'package:cake_wallet/exchange/trade_request.dart';
|
||||||
import 'package:cake_wallet/exchange/trade_state.dart';
|
import 'package:cake_wallet/exchange/trade_state.dart';
|
||||||
import 'package:cake_wallet/exchange/utils/currency_pairs_utils.dart';
|
import 'package:cake_wallet/exchange/utils/currency_pairs_utils.dart';
|
||||||
import 'package:cake_wallet/utils/device_info.dart';
|
import 'package:cake_wallet/utils/device_info.dart';
|
||||||
|
import 'package:cw_core/utils/proxy_wrapper.dart';
|
||||||
import 'package:cw_core/crypto_currency.dart';
|
import 'package:cw_core/crypto_currency.dart';
|
||||||
import 'package:http/http.dart';
|
|
||||||
|
|
||||||
class SimpleSwapExchangeProvider extends ExchangeProvider {
|
class SimpleSwapExchangeProvider extends ExchangeProvider {
|
||||||
SimpleSwapExchangeProvider() : super(pairList: supportedPairs(_notSupported));
|
SimpleSwapExchangeProvider() : super(pairList: supportedPairs(_notSupported));
|
||||||
|
@ -48,7 +48,7 @@ class SimpleSwapExchangeProvider extends ExchangeProvider {
|
||||||
@override
|
@override
|
||||||
Future<bool> checkIsAvailable() async {
|
Future<bool> checkIsAvailable() async {
|
||||||
final uri = Uri.https(apiAuthority, getEstimatePath, <String, String>{'api_key': apiKey});
|
final uri = Uri.https(apiAuthority, getEstimatePath, <String, String>{'api_key': apiKey});
|
||||||
final response = await get(uri);
|
final response = await ProxyWrapper().get(clearnetUri: uri);
|
||||||
|
|
||||||
return !(response.statusCode == 403);
|
return !(response.statusCode == 403);
|
||||||
}
|
}
|
||||||
|
@ -66,7 +66,8 @@ class SimpleSwapExchangeProvider extends ExchangeProvider {
|
||||||
};
|
};
|
||||||
final uri = Uri.https(apiAuthority, rangePath, params);
|
final uri = Uri.https(apiAuthority, rangePath, params);
|
||||||
|
|
||||||
final response = await get(uri);
|
final response = await ProxyWrapper().get(clearnetUri: uri);
|
||||||
|
|
||||||
|
|
||||||
if (response.statusCode == 500) {
|
if (response.statusCode == 500) {
|
||||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||||
|
@ -104,10 +105,10 @@ class SimpleSwapExchangeProvider extends ExchangeProvider {
|
||||||
'fixed': isFixedRateMode.toString()
|
'fixed': isFixedRateMode.toString()
|
||||||
};
|
};
|
||||||
final uri = Uri.https(apiAuthority, getEstimatePath, params);
|
final uri = Uri.https(apiAuthority, getEstimatePath, params);
|
||||||
final response = await get(uri);
|
final response = await ProxyWrapper().get(clearnetUri: uri);
|
||||||
|
|
||||||
|
|
||||||
if (response.body == "null") return 0.00;
|
if (response.body == "null") return 0.00;
|
||||||
|
|
||||||
final data = json.decode(response.body) as String;
|
final data = json.decode(response.body) as String;
|
||||||
|
|
||||||
return double.parse(data) / amount;
|
return double.parse(data) / amount;
|
||||||
|
@ -134,7 +135,12 @@ class SimpleSwapExchangeProvider extends ExchangeProvider {
|
||||||
};
|
};
|
||||||
final uri = Uri.https(apiAuthority, createExchangePath, params);
|
final uri = Uri.https(apiAuthority, createExchangePath, params);
|
||||||
|
|
||||||
final response = await post(uri, headers: headers, body: json.encode(body));
|
final response = await ProxyWrapper().post(
|
||||||
|
clearnetUri: uri,
|
||||||
|
headers: headers,
|
||||||
|
body: json.encode(body),
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
if (response.statusCode != 200 && response.statusCode != 201) {
|
if (response.statusCode != 200 && response.statusCode != 201) {
|
||||||
if (response.statusCode == 400) {
|
if (response.statusCode == 400) {
|
||||||
|
@ -176,8 +182,9 @@ class SimpleSwapExchangeProvider extends ExchangeProvider {
|
||||||
Future<Trade> findTradeById({required String id}) async {
|
Future<Trade> findTradeById({required String id}) async {
|
||||||
final params = {'api_key': apiKey, 'id': id};
|
final params = {'api_key': apiKey, 'id': id};
|
||||||
final uri = Uri.https(apiAuthority, getExchangePath, params);
|
final uri = Uri.https(apiAuthority, getExchangePath, params);
|
||||||
final response = await get(uri);
|
final response = await ProxyWrapper().get(clearnetUri: uri);
|
||||||
|
|
||||||
|
|
||||||
if (response.statusCode == 404) {
|
if (response.statusCode == 404) {
|
||||||
throw TradeNotFoundException(id, provider: description);
|
throw TradeNotFoundException(id, provider: description);
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,8 +10,8 @@ import 'package:cake_wallet/exchange/trade_not_created_exception.dart';
|
||||||
import 'package:cake_wallet/exchange/trade_request.dart';
|
import 'package:cake_wallet/exchange/trade_request.dart';
|
||||||
import 'package:cake_wallet/exchange/trade_state.dart';
|
import 'package:cake_wallet/exchange/trade_state.dart';
|
||||||
import 'package:cake_wallet/exchange/utils/currency_pairs_utils.dart';
|
import 'package:cake_wallet/exchange/utils/currency_pairs_utils.dart';
|
||||||
|
import 'package:cw_core/utils/proxy_wrapper.dart';
|
||||||
import 'package:cw_core/crypto_currency.dart';
|
import 'package:cw_core/crypto_currency.dart';
|
||||||
import 'package:http/http.dart' as http;
|
|
||||||
|
|
||||||
class StealthExExchangeProvider extends ExchangeProvider {
|
class StealthExExchangeProvider extends ExchangeProvider {
|
||||||
StealthExExchangeProvider() : super(pairList: supportedPairs(_notSupported));
|
StealthExExchangeProvider() : super(pairList: supportedPairs(_notSupported));
|
||||||
|
@ -63,8 +63,12 @@ class StealthExExchangeProvider extends ExchangeProvider {
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final response = await http.post(Uri.parse(_baseUrl + _rangePath),
|
final response = await ProxyWrapper().post(
|
||||||
headers: headers, body: json.encode(body));
|
clearnetUri: Uri.parse(_baseUrl + _rangePath),
|
||||||
|
headers: headers,
|
||||||
|
body: json.encode(body),
|
||||||
|
);
|
||||||
|
|
||||||
if (response.statusCode != 200) {
|
if (response.statusCode != 200) {
|
||||||
throw Exception('StealthEx fetch limits failed: ${response.body}');
|
throw Exception('StealthEx fetch limits failed: ${response.body}');
|
||||||
}
|
}
|
||||||
|
@ -134,8 +138,12 @@ class StealthExExchangeProvider extends ExchangeProvider {
|
||||||
'additional_fee_percent': _additionalFeePercent,
|
'additional_fee_percent': _additionalFeePercent,
|
||||||
};
|
};
|
||||||
|
|
||||||
final response = await http.post(Uri.parse(_baseUrl + _exchangesPath),
|
final response = await ProxyWrapper().post(
|
||||||
headers: headers, body: json.encode(body));
|
clearnetUri: Uri.parse(_baseUrl + _exchangesPath),
|
||||||
|
headers: headers,
|
||||||
|
body: json.encode(body),
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
if (response.statusCode != 201) {
|
if (response.statusCode != 201) {
|
||||||
throw Exception('StealthEx create trade failed: ${response.body}');
|
throw Exception('StealthEx create trade failed: ${response.body}');
|
||||||
|
@ -202,8 +210,9 @@ class StealthExExchangeProvider extends ExchangeProvider {
|
||||||
final headers = {'Authorization': apiKey, 'Content-Type': 'application/json'};
|
final headers = {'Authorization': apiKey, 'Content-Type': 'application/json'};
|
||||||
|
|
||||||
final uri = Uri.parse('$_baseUrl$_exchangesPath/$id');
|
final uri = Uri.parse('$_baseUrl$_exchangesPath/$id');
|
||||||
final response = await http.get(uri, headers: headers);
|
final response = await ProxyWrapper().get(clearnetUri: uri, headers: headers);
|
||||||
|
|
||||||
|
|
||||||
if (response.statusCode != 200) {
|
if (response.statusCode != 200) {
|
||||||
throw Exception('StealthEx fetch trade failed: ${response.body}');
|
throw Exception('StealthEx fetch trade failed: ${response.body}');
|
||||||
}
|
}
|
||||||
|
@ -260,8 +269,12 @@ class StealthExExchangeProvider extends ExchangeProvider {
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final response = await http.post(Uri.parse(_baseUrl + _amountPath),
|
final response = await ProxyWrapper().post(
|
||||||
headers: headers, body: json.encode(body));
|
clearnetUri: Uri.parse(_baseUrl + _amountPath),
|
||||||
|
headers: headers,
|
||||||
|
body: json.encode(body),
|
||||||
|
);
|
||||||
|
|
||||||
if (response.statusCode != 200) return {};
|
if (response.statusCode != 200) return {};
|
||||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||||
final rate = responseJSON['rate'] as Map<String, dynamic>?;
|
final rate = responseJSON['rate'] as Map<String, dynamic>?;
|
||||||
|
|
|
@ -10,9 +10,9 @@ import 'package:cake_wallet/exchange/trade_not_found_exception.dart';
|
||||||
import 'package:cake_wallet/exchange/trade_request.dart';
|
import 'package:cake_wallet/exchange/trade_request.dart';
|
||||||
import 'package:cake_wallet/exchange/trade_state.dart';
|
import 'package:cake_wallet/exchange/trade_state.dart';
|
||||||
import 'package:cake_wallet/exchange/utils/currency_pairs_utils.dart';
|
import 'package:cake_wallet/exchange/utils/currency_pairs_utils.dart';
|
||||||
|
import 'package:cw_core/utils/proxy_wrapper.dart';
|
||||||
import 'package:cw_core/crypto_currency.dart';
|
import 'package:cw_core/crypto_currency.dart';
|
||||||
import 'package:cw_core/utils/print_verbose.dart';
|
import 'package:cw_core/utils/print_verbose.dart';
|
||||||
import 'package:http/http.dart';
|
|
||||||
|
|
||||||
class SwapTradeExchangeProvider extends ExchangeProvider {
|
class SwapTradeExchangeProvider extends ExchangeProvider {
|
||||||
SwapTradeExchangeProvider() : super(pairList: supportedPairs(_notSupported));
|
SwapTradeExchangeProvider() : super(pairList: supportedPairs(_notSupported));
|
||||||
|
@ -69,7 +69,8 @@ class SwapTradeExchangeProvider extends ExchangeProvider {
|
||||||
}) async {
|
}) async {
|
||||||
try {
|
try {
|
||||||
final uri = Uri.https(apiAuthority, getCoins);
|
final uri = Uri.https(apiAuthority, getCoins);
|
||||||
final response = await get(uri);
|
final response = await ProxyWrapper().get(clearnetUri: uri);
|
||||||
|
|
||||||
|
|
||||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||||
|
|
||||||
|
@ -116,7 +117,12 @@ class SwapTradeExchangeProvider extends ExchangeProvider {
|
||||||
};
|
};
|
||||||
|
|
||||||
final uri = Uri.https(apiAuthority, getRate, params);
|
final uri = Uri.https(apiAuthority, getRate, params);
|
||||||
final response = await post(uri, body: body, headers: headers);
|
final response = await ProxyWrapper().post(
|
||||||
|
clearnetUri: uri,
|
||||||
|
body: json.encode(body),
|
||||||
|
headers: headers,
|
||||||
|
);
|
||||||
|
|
||||||
final responseBody = json.decode(response.body) as Map<String, dynamic>;
|
final responseBody = json.decode(response.body) as Map<String, dynamic>;
|
||||||
|
|
||||||
if (response.statusCode != 200)
|
if (response.statusCode != 200)
|
||||||
|
@ -153,7 +159,12 @@ class SwapTradeExchangeProvider extends ExchangeProvider {
|
||||||
};
|
};
|
||||||
|
|
||||||
final uri = Uri.https(apiAuthority, createOrder, params);
|
final uri = Uri.https(apiAuthority, createOrder, params);
|
||||||
final response = await post(uri, body: body, headers: headers);
|
final response = await ProxyWrapper().post(
|
||||||
|
clearnetUri: uri,
|
||||||
|
body: json.encode(body),
|
||||||
|
headers: headers,
|
||||||
|
);
|
||||||
|
|
||||||
final responseBody = json.decode(response.body) as Map<String, dynamic>;
|
final responseBody = json.decode(response.body) as Map<String, dynamic>;
|
||||||
|
|
||||||
if (response.statusCode == 400 || responseBody["success"] == false) {
|
if (response.statusCode == 400 || responseBody["success"] == false) {
|
||||||
|
@ -196,7 +207,12 @@ class SwapTradeExchangeProvider extends ExchangeProvider {
|
||||||
};
|
};
|
||||||
|
|
||||||
final uri = Uri.https(apiAuthority, order, params);
|
final uri = Uri.https(apiAuthority, order, params);
|
||||||
final response = await post(uri, body: body, headers: headers);
|
final response = await ProxyWrapper().post(
|
||||||
|
clearnetUri: uri,
|
||||||
|
body: json.encode(body),
|
||||||
|
headers: headers,
|
||||||
|
);
|
||||||
|
|
||||||
final responseBody = json.decode(response.body) as Map<String, dynamic>;
|
final responseBody = json.decode(response.body) as Map<String, dynamic>;
|
||||||
|
|
||||||
if (response.statusCode == 400 || responseBody["success"] == false) {
|
if (response.statusCode == 400 || responseBody["success"] == false) {
|
||||||
|
|
|
@ -7,10 +7,10 @@ import 'package:cake_wallet/exchange/trade.dart';
|
||||||
import 'package:cake_wallet/exchange/trade_request.dart';
|
import 'package:cake_wallet/exchange/trade_request.dart';
|
||||||
import 'package:cake_wallet/exchange/trade_state.dart';
|
import 'package:cake_wallet/exchange/trade_state.dart';
|
||||||
import 'package:cake_wallet/exchange/utils/currency_pairs_utils.dart';
|
import 'package:cake_wallet/exchange/utils/currency_pairs_utils.dart';
|
||||||
|
import 'package:cw_core/utils/proxy_wrapper.dart';
|
||||||
import 'package:cw_core/crypto_currency.dart';
|
import 'package:cw_core/crypto_currency.dart';
|
||||||
import 'package:cw_core/utils/print_verbose.dart';
|
import 'package:cw_core/utils/print_verbose.dart';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
import 'package:http/http.dart' as http;
|
|
||||||
|
|
||||||
class ThorChainExchangeProvider extends ExchangeProvider {
|
class ThorChainExchangeProvider extends ExchangeProvider {
|
||||||
ThorChainExchangeProvider({required this.tradesStore})
|
ThorChainExchangeProvider({required this.tradesStore})
|
||||||
|
@ -164,7 +164,8 @@ class ThorChainExchangeProvider extends ExchangeProvider {
|
||||||
if (id.isEmpty) throw Exception('Trade id is empty');
|
if (id.isEmpty) throw Exception('Trade id is empty');
|
||||||
final formattedId = id.startsWith('0x') ? id.substring(2) : id;
|
final formattedId = id.startsWith('0x') ? id.substring(2) : id;
|
||||||
final uri = Uri.https(_baseNodeURL, '$_txInfoPath$formattedId');
|
final uri = Uri.https(_baseNodeURL, '$_txInfoPath$formattedId');
|
||||||
final response = await http.get(uri);
|
final response = await ProxyWrapper().get(clearnetUri: uri);
|
||||||
|
|
||||||
|
|
||||||
if (response.statusCode == 404) {
|
if (response.statusCode == 404) {
|
||||||
throw Exception('Trade not found for id: $formattedId');
|
throw Exception('Trade not found for id: $formattedId');
|
||||||
|
@ -217,8 +218,8 @@ class ThorChainExchangeProvider extends ExchangeProvider {
|
||||||
|
|
||||||
static Future<Map<String, String>?>? lookupAddressByName(String name) async {
|
static Future<Map<String, String>?>? lookupAddressByName(String name) async {
|
||||||
final uri = Uri.https(_baseURL, '$_nameLookUpPath$name');
|
final uri = Uri.https(_baseURL, '$_nameLookUpPath$name');
|
||||||
final response = await http.get(uri);
|
final response = await ProxyWrapper().get(clearnetUri: uri);
|
||||||
|
|
||||||
if (response.statusCode != 200) {
|
if (response.statusCode != 200) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -244,8 +245,8 @@ class ThorChainExchangeProvider extends ExchangeProvider {
|
||||||
Future<Map<String, dynamic>> _getSwapQuote(Map<String, String> params) async {
|
Future<Map<String, dynamic>> _getSwapQuote(Map<String, String> params) async {
|
||||||
Uri uri = Uri.https(_baseNodeURL, _quotePath, params);
|
Uri uri = Uri.https(_baseNodeURL, _quotePath, params);
|
||||||
|
|
||||||
final response = await http.get(uri);
|
final response = await ProxyWrapper().get(clearnetUri: uri);
|
||||||
|
|
||||||
if (response.statusCode != 200) {
|
if (response.statusCode != 200) {
|
||||||
throw Exception('Unexpected HTTP status: ${response.statusCode}');
|
throw Exception('Unexpected HTTP status: ${response.statusCode}');
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,9 +8,9 @@ import 'package:cake_wallet/exchange/trade.dart';
|
||||||
import 'package:cake_wallet/exchange/trade_request.dart';
|
import 'package:cake_wallet/exchange/trade_request.dart';
|
||||||
import 'package:cake_wallet/exchange/trade_state.dart';
|
import 'package:cake_wallet/exchange/trade_state.dart';
|
||||||
import 'package:cake_wallet/exchange/utils/currency_pairs_utils.dart';
|
import 'package:cake_wallet/exchange/utils/currency_pairs_utils.dart';
|
||||||
|
import 'package:cw_core/utils/proxy_wrapper.dart';
|
||||||
import 'package:cw_core/crypto_currency.dart';
|
import 'package:cw_core/crypto_currency.dart';
|
||||||
import 'package:cw_core/utils/print_verbose.dart';
|
import 'package:cw_core/utils/print_verbose.dart';
|
||||||
import 'package:http/http.dart';
|
|
||||||
|
|
||||||
class TrocadorExchangeProvider extends ExchangeProvider {
|
class TrocadorExchangeProvider extends ExchangeProvider {
|
||||||
TrocadorExchangeProvider({this.useTorOnly = false, this.providerStates = const {}})
|
TrocadorExchangeProvider({this.useTorOnly = false, this.providerStates = const {}})
|
||||||
|
@ -52,8 +52,9 @@ class TrocadorExchangeProvider extends ExchangeProvider {
|
||||||
];
|
];
|
||||||
|
|
||||||
static const apiKey = secrets.trocadorApiKey;
|
static const apiKey = secrets.trocadorApiKey;
|
||||||
static const onionApiAuthority = 'trocadorfyhlu27aefre5u7zri66gudtzdyelymftvr4yjwcxhfaqsid.onion';
|
|
||||||
static const clearNetAuthority = 'api.trocador.app';
|
static const clearNetAuthority = 'api.trocador.app';
|
||||||
|
static const onionApiAuthority = clearNetAuthority;
|
||||||
|
// static const onionApiAuthority = 'trocadorfyhlu27aefre5u7zri66gudtzdyelymftvr4yjwcxhfaqsid.onion';
|
||||||
static const markup = secrets.trocadorExchangeMarkup;
|
static const markup = secrets.trocadorExchangeMarkup;
|
||||||
static const newRatePath = '/new_rate';
|
static const newRatePath = '/new_rate';
|
||||||
static const createTradePath = '/new_trade';
|
static const createTradePath = '/new_trade';
|
||||||
|
@ -97,7 +98,8 @@ class TrocadorExchangeProvider extends ExchangeProvider {
|
||||||
};
|
};
|
||||||
|
|
||||||
final uri = await _getUri(coinPath, params);
|
final uri = await _getUri(coinPath, params);
|
||||||
final response = await get(uri, headers: {'API-Key': apiKey});
|
final response = await ProxyWrapper().get(clearnetUri: uri, headers: {'API-Key': apiKey});
|
||||||
|
|
||||||
|
|
||||||
if (response.statusCode != 200)
|
if (response.statusCode != 200)
|
||||||
throw Exception('Unexpected http status: ${response.statusCode}');
|
throw Exception('Unexpected http status: ${response.statusCode}');
|
||||||
|
@ -138,12 +140,10 @@ class TrocadorExchangeProvider extends ExchangeProvider {
|
||||||
};
|
};
|
||||||
|
|
||||||
final uri = await _getUri(newRatePath, params);
|
final uri = await _getUri(newRatePath, params);
|
||||||
final response = await get(uri, headers: {'API-Key': apiKey});
|
final response = await ProxyWrapper().get(clearnetUri: uri, headers: {'API-Key': apiKey});
|
||||||
|
|
||||||
|
|
||||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||||
|
|
||||||
if (responseJSON['error'] != null) throw Exception(responseJSON['error']);
|
|
||||||
|
|
||||||
final fromAmount = double.parse(responseJSON['amount_from'].toString());
|
final fromAmount = double.parse(responseJSON['amount_from'].toString());
|
||||||
final toAmount = double.parse(responseJSON['amount_to'].toString());
|
final toAmount = double.parse(responseJSON['amount_to'].toString());
|
||||||
final rateId = responseJSON['trade_id'] as String? ?? '';
|
final rateId = responseJSON['trade_id'] as String? ?? '';
|
||||||
|
@ -206,8 +206,9 @@ class TrocadorExchangeProvider extends ExchangeProvider {
|
||||||
params['provider'] = _provider.first as String;
|
params['provider'] = _provider.first as String;
|
||||||
|
|
||||||
final uri = await _getUri(createTradePath, params);
|
final uri = await _getUri(createTradePath, params);
|
||||||
final response = await get(uri, headers: {'API-Key': apiKey});
|
final response = await ProxyWrapper().get(clearnetUri: uri, headers: {'API-Key': apiKey});
|
||||||
|
|
||||||
|
|
||||||
if (response.statusCode == 400) {
|
if (response.statusCode == 400) {
|
||||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||||
final error = responseJSON['error'] as String;
|
final error = responseJSON['error'] as String;
|
||||||
|
@ -230,6 +231,7 @@ class TrocadorExchangeProvider extends ExchangeProvider {
|
||||||
final providerName = responseJSON['provider'] as String;
|
final providerName = responseJSON['provider'] as String;
|
||||||
final amount = responseJSON['amount_from']?.toString();
|
final amount = responseJSON['amount_from']?.toString();
|
||||||
final receiveAmount = responseJSON['amount_to']?.toString();
|
final receiveAmount = responseJSON['amount_to']?.toString();
|
||||||
|
final addressProviderMemo = responseJSON['address_provider_memo'] as String?;
|
||||||
|
|
||||||
return Trade(
|
return Trade(
|
||||||
id: id,
|
id: id,
|
||||||
|
@ -247,15 +249,17 @@ class TrocadorExchangeProvider extends ExchangeProvider {
|
||||||
receiveAmount: receiveAmount ?? request.toAmount,
|
receiveAmount: receiveAmount ?? request.toAmount,
|
||||||
payoutAddress: payoutAddress,
|
payoutAddress: payoutAddress,
|
||||||
isSendAll: isSendAll,
|
isSendAll: isSendAll,
|
||||||
|
extraId: addressProviderMemo,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Trade> findTradeById({required String id}) async {
|
Future<Trade> findTradeById({required String id}) async {
|
||||||
final uri = await _getUri(tradePath, {'id': id});
|
final uri = await _getUri(tradePath, {'id': id});
|
||||||
return get(uri, headers: {'API-Key': apiKey}).then((response) {
|
return ProxyWrapper().get(clearnetUri: uri, headers: {'API-Key': apiKey}).then((response) async {
|
||||||
if (response.statusCode != 200)
|
if (response.statusCode != 200)
|
||||||
throw Exception('Unexpected http status: ${response.statusCode}');
|
throw Exception('Unexpected http status: ${response.statusCode}');
|
||||||
|
|
||||||
|
|
||||||
final responseListJson = json.decode(response.body) as List;
|
final responseListJson = json.decode(response.body) as List;
|
||||||
final responseJSON = responseListJson.first;
|
final responseJSON = responseListJson.first;
|
||||||
|
@ -290,7 +294,8 @@ class TrocadorExchangeProvider extends ExchangeProvider {
|
||||||
|
|
||||||
Future<List<TrocadorPartners>> fetchProviders() async {
|
Future<List<TrocadorPartners>> fetchProviders() async {
|
||||||
final uri = await _getUri(providersListPath, {'api_key': apiKey});
|
final uri = await _getUri(providersListPath, {'api_key': apiKey});
|
||||||
final response = await get(uri);
|
final response = await ProxyWrapper().get(clearnetUri: uri);
|
||||||
|
|
||||||
|
|
||||||
if (response.statusCode != 200)
|
if (response.statusCode != 200)
|
||||||
throw Exception('Unexpected http status: ${response.statusCode}');
|
throw Exception('Unexpected http status: ${response.statusCode}');
|
||||||
|
@ -353,7 +358,7 @@ class TrocadorExchangeProvider extends ExchangeProvider {
|
||||||
if (useTorOnly) return uri;
|
if (useTorOnly) return uri;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await get(uri);
|
await ProxyWrapper().get(clearnetUri: uri);
|
||||||
|
|
||||||
return uri;
|
return uri;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
|
@ -10,8 +10,7 @@ import 'package:cake_wallet/exchange/trade_state.dart';
|
||||||
import 'package:cake_wallet/exchange/utils/currency_pairs_utils.dart';
|
import 'package:cake_wallet/exchange/utils/currency_pairs_utils.dart';
|
||||||
import 'package:cw_core/crypto_currency.dart';
|
import 'package:cw_core/crypto_currency.dart';
|
||||||
import 'package:cw_core/utils/print_verbose.dart';
|
import 'package:cw_core/utils/print_verbose.dart';
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:cw_core/utils/proxy_wrapper.dart';
|
||||||
|
|
||||||
class XOSwapExchangeProvider extends ExchangeProvider {
|
class XOSwapExchangeProvider extends ExchangeProvider {
|
||||||
XOSwapExchangeProvider() : super(pairList: supportedPairs(_notSupported));
|
XOSwapExchangeProvider() : super(pairList: supportedPairs(_notSupported));
|
||||||
|
|
||||||
|
@ -72,7 +71,8 @@ class XOSwapExchangeProvider extends ExchangeProvider {
|
||||||
final uri = Uri.https(_apiAuthority, _apiPath + _assets,
|
final uri = Uri.https(_apiAuthority, _apiPath + _assets,
|
||||||
{'networks': normalizedNetwork, 'query': currency.title});
|
{'networks': normalizedNetwork, 'query': currency.title});
|
||||||
|
|
||||||
final response = await http.get(uri, headers: _headers);
|
final response = await ProxyWrapper().get(clearnetUri: uri);
|
||||||
|
|
||||||
if (response.statusCode != 200) {
|
if (response.statusCode != 200) {
|
||||||
throw Exception('Failed to fetch assets for ${currency.title} on ${currency.tag}');
|
throw Exception('Failed to fetch assets for ${currency.title} on ${currency.tag}');
|
||||||
}
|
}
|
||||||
|
@ -102,7 +102,8 @@ class XOSwapExchangeProvider extends ExchangeProvider {
|
||||||
if (curFrom == null || curTo == null) return [];
|
if (curFrom == null || curTo == null) return [];
|
||||||
final pairId = curFrom + '_' + curTo;
|
final pairId = curFrom + '_' + curTo;
|
||||||
final uri = Uri.https(_apiAuthority, '$_apiPath$_pairsPath/$pairId$_ratePath');
|
final uri = Uri.https(_apiAuthority, '$_apiPath$_pairsPath/$pairId$_ratePath');
|
||||||
final response = await http.get(uri, headers: _headers);
|
final response = await ProxyWrapper().get(clearnetUri: uri);
|
||||||
|
|
||||||
if (response.statusCode != 200) return [];
|
if (response.statusCode != 200) return [];
|
||||||
return json.decode(response.body) as List<dynamic>;
|
return json.decode(response.body) as List<dynamic>;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -205,7 +206,12 @@ class XOSwapExchangeProvider extends ExchangeProvider {
|
||||||
'pairId': pairId,
|
'pairId': pairId,
|
||||||
};
|
};
|
||||||
|
|
||||||
final response = await http.post(uri, headers: _headers, body: json.encode(payload));
|
final response = await ProxyWrapper().post(
|
||||||
|
clearnetUri: uri,
|
||||||
|
headers: _headers,
|
||||||
|
body: json.encode(payload),
|
||||||
|
);
|
||||||
|
|
||||||
if (response.statusCode != 201) {
|
if (response.statusCode != 201) {
|
||||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||||
final error = responseJSON['error'] ?? 'Unknown error';
|
final error = responseJSON['error'] ?? 'Unknown error';
|
||||||
|
@ -254,7 +260,8 @@ class XOSwapExchangeProvider extends ExchangeProvider {
|
||||||
Future<Trade> findTradeById({required String id}) async {
|
Future<Trade> findTradeById({required String id}) async {
|
||||||
try {
|
try {
|
||||||
final uri = Uri.https(_apiAuthority, '$_apiPath$_orders/$id');
|
final uri = Uri.https(_apiAuthority, '$_apiPath$_orders/$id');
|
||||||
final response = await http.get(uri, headers: _headers);
|
final response = await ProxyWrapper().get(clearnetUri: uri);
|
||||||
|
|
||||||
if (response.statusCode != 200) {
|
if (response.statusCode != 200) {
|
||||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||||
if (responseJSON.containsKey('code') && responseJSON['code'] == 'NOT_FOUND') {
|
if (responseJSON.containsKey('code') && responseJSON['code'] == 'NOT_FOUND') {
|
||||||
|
|
|
@ -25,9 +25,12 @@ import 'package:cake_wallet/routes.dart';
|
||||||
import 'package:cake_wallet/src/screens/root/root.dart';
|
import 'package:cake_wallet/src/screens/root/root.dart';
|
||||||
import 'package:cake_wallet/store/app_store.dart';
|
import 'package:cake_wallet/store/app_store.dart';
|
||||||
import 'package:cake_wallet/store/authentication_store.dart';
|
import 'package:cake_wallet/store/authentication_store.dart';
|
||||||
|
import 'package:cake_wallet/themes/core/material_base_theme.dart';
|
||||||
import 'package:cake_wallet/themes/utils/theme_provider.dart';
|
import 'package:cake_wallet/themes/utils/theme_provider.dart';
|
||||||
|
import 'package:cake_wallet/store/settings_store.dart';
|
||||||
import 'package:cake_wallet/utils/device_info.dart';
|
import 'package:cake_wallet/utils/device_info.dart';
|
||||||
import 'package:cake_wallet/utils/exception_handler.dart';
|
import 'package:cake_wallet/utils/exception_handler.dart';
|
||||||
|
import 'package:cake_wallet/utils/feature_flag.dart';
|
||||||
import 'package:cake_wallet/view_model/link_view_model.dart';
|
import 'package:cake_wallet/view_model/link_view_model.dart';
|
||||||
import 'package:cake_wallet/utils/responsive_layout_util.dart';
|
import 'package:cake_wallet/utils/responsive_layout_util.dart';
|
||||||
import 'package:cw_core/address_info.dart';
|
import 'package:cw_core/address_info.dart';
|
||||||
|
@ -38,6 +41,8 @@ import 'package:cw_core/node.dart';
|
||||||
import 'package:cw_core/payjoin_session.dart';
|
import 'package:cw_core/payjoin_session.dart';
|
||||||
import 'package:cw_core/unspent_coins_info.dart';
|
import 'package:cw_core/unspent_coins_info.dart';
|
||||||
import 'package:cw_core/utils/print_verbose.dart';
|
import 'package:cw_core/utils/print_verbose.dart';
|
||||||
|
import 'package:cw_core/utils/proxy_logger/memory_proxy_logger.dart';
|
||||||
|
import 'package:cw_core/utils/proxy_wrapper.dart';
|
||||||
import 'package:cw_core/wallet_info.dart';
|
import 'package:cw_core/wallet_info.dart';
|
||||||
import 'package:cw_core/wallet_type.dart';
|
import 'package:cw_core/wallet_type.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
|
@ -87,6 +92,9 @@ Future<void> runAppWithZone({Key? topLevelKey}) async {
|
||||||
ledgerFile.writeAsStringSync("$content\n${event.message}");
|
ledgerFile.writeAsStringSync("$content\n${event.message}");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
if (FeatureFlag.hasDevOptions) {
|
||||||
|
ProxyWrapper.logger = MemoryProxyLogger();
|
||||||
|
}
|
||||||
|
|
||||||
runApp(App(key: topLevelKey));
|
runApp(App(key: topLevelKey));
|
||||||
isAppRunning = true;
|
isAppRunning = true;
|
||||||
|
@ -194,8 +202,8 @@ Future<void> initializeAppConfigs({bool loadWallet = true}) async {
|
||||||
final powNodes =
|
final powNodes =
|
||||||
await CakeHive.openBox<Node>(Node.boxName + "pow"); // must be different from Node.boxName
|
await CakeHive.openBox<Node>(Node.boxName + "pow"); // must be different from Node.boxName
|
||||||
final transactionDescriptions = await CakeHive.openBox<TransactionDescription>(
|
final transactionDescriptions = await CakeHive.openBox<TransactionDescription>(
|
||||||
TransactionDescription.boxName,
|
TransactionDescription.boxName,
|
||||||
encryptionKey: transactionDescriptionsBoxKey);
|
encryptionKey: transactionDescriptionsBoxKey);
|
||||||
final trades = await CakeHive.openBox<Trade>(Trade.boxName, encryptionKey: tradesBoxKey);
|
final trades = await CakeHive.openBox<Trade>(Trade.boxName, encryptionKey: tradesBoxKey);
|
||||||
final orders = await CakeHive.openBox<Order>(Order.boxName, encryptionKey: ordersBoxKey);
|
final orders = await CakeHive.openBox<Order>(Order.boxName, encryptionKey: ordersBoxKey);
|
||||||
final walletInfoSource = await CakeHive.openBox<WalletInfo>(WalletInfo.boxName);
|
final walletInfoSource = await CakeHive.openBox<WalletInfo>(WalletInfo.boxName);
|
||||||
|
@ -279,7 +287,11 @@ Future<void> initialSetup({
|
||||||
navigatorKey: navigatorKey,
|
navigatorKey: navigatorKey,
|
||||||
secureStorage: secureStorage,
|
secureStorage: secureStorage,
|
||||||
);
|
);
|
||||||
await bootstrap(navigatorKey, loadWallet: loadWallet);
|
await bootstrapOffline();
|
||||||
|
final settingsStore = getIt<SettingsStore>();
|
||||||
|
if (!settingsStore.currentBuiltinTor) {
|
||||||
|
bootstrapOnline(navigatorKey, loadWallet: loadWallet);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class App extends StatefulWidget {
|
class App extends StatefulWidget {
|
||||||
|
@ -293,20 +305,24 @@ class App extends StatefulWidget {
|
||||||
class AppState extends State<App> with SingleTickerProviderStateMixin {
|
class AppState extends State<App> with SingleTickerProviderStateMixin {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Observer(
|
return Observer(builder: (BuildContext context) {
|
||||||
builder: (BuildContext context) {
|
|
||||||
final appStore = getIt.get<AppStore>();
|
final appStore = getIt.get<AppStore>();
|
||||||
final authService = getIt.get<AuthService>();
|
final authService = getIt.get<AuthService>();
|
||||||
final linkViewModel = getIt.get<LinkViewModel>();
|
final linkViewModel = getIt.get<LinkViewModel>();
|
||||||
final tradeMonitor = getIt.get<TradeMonitor>();
|
final tradeMonitor = getIt.get<TradeMonitor>();
|
||||||
|
final settingsStore = appStore.settingsStore;
|
||||||
final statusBarColor = Colors.transparent;
|
final statusBarColor = Colors.transparent;
|
||||||
final authenticationStore = getIt.get<AuthenticationStore>();
|
final authenticationStore = getIt.get<AuthenticationStore>();
|
||||||
final initialRoute = authenticationStore.state == AuthenticationState.uninitialized
|
final initialRoute = authenticationStore.state == AuthenticationState.uninitialized
|
||||||
? Routes.welcome
|
? Routes.welcome
|
||||||
: Routes.login;
|
: settingsStore.currentBuiltinTor ? Routes.startTor : Routes.login;
|
||||||
final currentTheme = appStore.themeStore.currentTheme;
|
final currentTheme = appStore.themeStore.currentTheme;
|
||||||
final statusBarBrightness = currentTheme.isDark ? Brightness.light : Brightness.dark;
|
final statusBarBrightness = currentTheme.type == currentTheme.isDark
|
||||||
final statusBarIconBrightness = currentTheme.isDark ? Brightness.light : Brightness.dark;
|
? Brightness.light
|
||||||
|
: Brightness.dark;
|
||||||
|
final statusBarIconBrightness = currentTheme.type == currentTheme.isDark
|
||||||
|
? Brightness.light
|
||||||
|
: Brightness.dark;
|
||||||
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
|
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
|
||||||
statusBarColor: statusBarColor,
|
statusBarColor: statusBarColor,
|
||||||
statusBarBrightness: statusBarBrightness,
|
statusBarBrightness: statusBarBrightness,
|
||||||
|
@ -322,7 +338,8 @@ class AppState extends State<App> with SingleTickerProviderStateMixin {
|
||||||
tradeMonitor: tradeMonitor,
|
tradeMonitor: tradeMonitor,
|
||||||
child: ThemeProvider(
|
child: ThemeProvider(
|
||||||
themeStore: appStore.themeStore,
|
themeStore: appStore.themeStore,
|
||||||
materialAppBuilder: (context, theme, darkTheme, themeMode) => MaterialApp(
|
materialAppBuilder: (context, theme, darkTheme, themeMode) =>
|
||||||
|
MaterialApp(
|
||||||
navigatorObservers: [routeObserver],
|
navigatorObservers: [routeObserver],
|
||||||
navigatorKey: navigatorKey,
|
navigatorKey: navigatorKey,
|
||||||
debugShowCheckedModeBanner: false,
|
debugShowCheckedModeBanner: false,
|
||||||
|
@ -365,8 +382,10 @@ class _HomeState extends State<_Home> {
|
||||||
SystemChrome.setPreferredOrientations(
|
SystemChrome.setPreferredOrientations(
|
||||||
[DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]);
|
[DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]);
|
||||||
} else {
|
} else {
|
||||||
SystemChrome.setPreferredOrientations(
|
SystemChrome.setPreferredOrientations([
|
||||||
[DeviceOrientation.landscapeLeft, DeviceOrientation.landscapeRight]);
|
DeviceOrientation.landscapeLeft,
|
||||||
|
DeviceOrientation.landscapeRight
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
import 'package:cw_core/utils/proxy_wrapper.dart';
|
||||||
import 'package:cw_core/utils/print_verbose.dart';
|
import 'package:cw_core/utils/print_verbose.dart';
|
||||||
import 'package:http/http.dart' as http;
|
|
||||||
import 'package:cake_wallet/mastodon/mastodon_user.dart';
|
import 'package:cake_wallet/mastodon/mastodon_user.dart';
|
||||||
|
|
||||||
class MastodonAPI {
|
class MastodonAPI {
|
||||||
|
@ -20,7 +20,8 @@ class MastodonAPI {
|
||||||
queryParameters: queryParams,
|
queryParameters: queryParams,
|
||||||
);
|
);
|
||||||
|
|
||||||
final response = await http.get(uri);
|
final response = await ProxyWrapper().get(clearnetUri: uri);
|
||||||
|
|
||||||
|
|
||||||
if (response.statusCode != 200) return null;
|
if (response.statusCode != 200) return null;
|
||||||
|
|
||||||
|
@ -47,7 +48,8 @@ class MastodonAPI {
|
||||||
queryParameters: queryParams,
|
queryParameters: queryParams,
|
||||||
);
|
);
|
||||||
|
|
||||||
final response = await http.get(uri);
|
final response = await ProxyWrapper().get(clearnetUri: uri);
|
||||||
|
|
||||||
|
|
||||||
if (response.statusCode != 200) {
|
if (response.statusCode != 200) {
|
||||||
throw Exception('Unexpected HTTP status: ${response.statusCode}');
|
throw Exception('Unexpected HTTP status: ${response.statusCode}');
|
||||||
|
|
|
@ -365,7 +365,7 @@ class CWMonero extends Monero {
|
||||||
@override
|
@override
|
||||||
Map<String, String> pendingTransactionInfo(Object transaction) {
|
Map<String, String> pendingTransactionInfo(Object transaction) {
|
||||||
final ptx = transaction as PendingMoneroTransaction;
|
final ptx = transaction as PendingMoneroTransaction;
|
||||||
return {'id': ptx.id, 'hex': ptx.hex, 'key': ptx.txKey};
|
return {'id': ptx.id, 'hex': ptx.hex};
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue