mirror of
https://github.com/cake-tech/cake_wallet.git
synced 2025-06-29 04:49:51 +00:00
Merge branch 'main' of https://github.com/cake-tech/cake_wallet into mweb
This commit is contained in:
commit
fbffda74dd
32 changed files with 585 additions and 163 deletions
2
.github/workflows/pr_test_build_android.yml
vendored
2
.github/workflows/pr_test_build_android.yml
vendored
|
@ -195,6 +195,8 @@ jobs:
|
||||||
echo "const nanoNowNodesApiKey = '${{ secrets.NANO_NOW_NODES_API_KEY }}';" >> cw_nano/lib/.secrets.g.dart
|
echo "const nanoNowNodesApiKey = '${{ secrets.NANO_NOW_NODES_API_KEY }}';" >> cw_nano/lib/.secrets.g.dart
|
||||||
echo "const tronGridApiKey = '${{ secrets.TRON_GRID_API_KEY }}';" >> cw_tron/lib/.secrets.g.dart
|
echo "const tronGridApiKey = '${{ secrets.TRON_GRID_API_KEY }}';" >> cw_tron/lib/.secrets.g.dart
|
||||||
echo "const tronNowNodesApiKey = '${{ secrets.TRON_NOW_NODES_API_KEY }}';" >> cw_tron/lib/.secrets.g.dart
|
echo "const tronNowNodesApiKey = '${{ secrets.TRON_NOW_NODES_API_KEY }}';" >> cw_tron/lib/.secrets.g.dart
|
||||||
|
echo "const stealthExBearerToken = '${{ secrets.STEALTH_EX_BEARER_TOKEN }}';" >> lib/.secrets.g.dart
|
||||||
|
echo "const stealthExAdditionalFeePercent = '${{ secrets.STEALTH_EX_ADDITIONAL_FEE_PERCENT }}';" >> lib/.secrets.g.dart
|
||||||
|
|
||||||
- name: Rename app
|
- name: Rename app
|
||||||
run: |
|
run: |
|
||||||
|
|
2
.github/workflows/pr_test_build_linux.yml
vendored
2
.github/workflows/pr_test_build_linux.yml
vendored
|
@ -180,6 +180,8 @@ jobs:
|
||||||
echo "const nanoNowNodesApiKey = '${{ secrets.NANO_NOW_NODES_API_KEY }}';" >> cw_nano/lib/.secrets.g.dart
|
echo "const nanoNowNodesApiKey = '${{ secrets.NANO_NOW_NODES_API_KEY }}';" >> cw_nano/lib/.secrets.g.dart
|
||||||
echo "const tronGridApiKey = '${{ secrets.TRON_GRID_API_KEY }}';" >> cw_tron/lib/.secrets.g.dart
|
echo "const tronGridApiKey = '${{ secrets.TRON_GRID_API_KEY }}';" >> cw_tron/lib/.secrets.g.dart
|
||||||
echo "const tronNowNodesApiKey = '${{ secrets.TRON_NOW_NODES_API_KEY }}';" >> cw_tron/lib/.secrets.g.dart
|
echo "const tronNowNodesApiKey = '${{ secrets.TRON_NOW_NODES_API_KEY }}';" >> cw_tron/lib/.secrets.g.dart
|
||||||
|
echo "const stealthExBearerToken = '${{ secrets.STEALTH_EX_BEARER_TOKEN }}';" >> lib/.secrets.g.dart
|
||||||
|
echo "const stealthExAdditionalFeePercent = '${{ secrets.STEALTH_EX_ADDITIONAL_FEE_PERCENT }}';" >> lib/.secrets.g.dart
|
||||||
|
|
||||||
- name: Rename app
|
- name: Rename app
|
||||||
run: |
|
run: |
|
||||||
|
|
BIN
assets/images/stealthex.png
Normal file
BIN
assets/images/stealthex.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.4 KiB |
|
@ -17,6 +17,3 @@
|
||||||
-
|
-
|
||||||
uri: node.community.rino.io:18081
|
uri: node.community.rino.io:18081
|
||||||
is_default: false
|
is_default: false
|
||||||
-
|
|
||||||
uri: node.moneroworld.com:18089
|
|
||||||
is_default: false
|
|
||||||
|
|
|
@ -138,11 +138,17 @@ PendingTransactionDescription createTransactionMultDestSync(
|
||||||
int accountIndex = 0,
|
int accountIndex = 0,
|
||||||
List<String> preferredInputs = const []}) {
|
List<String> preferredInputs = const []}) {
|
||||||
|
|
||||||
|
final dstAddrs = outputs.map((e) => e.address).toList();
|
||||||
|
final amounts = outputs.map((e) => monero.Wallet_amountFromString(e.amount)).toList();
|
||||||
|
|
||||||
|
// print("multDest: dstAddrs: $dstAddrs");
|
||||||
|
// print("multDest: amounts: $amounts");
|
||||||
|
|
||||||
final txptr = monero.Wallet_createTransactionMultDest(
|
final txptr = monero.Wallet_createTransactionMultDest(
|
||||||
wptr!,
|
wptr!,
|
||||||
dstAddr: outputs.map((e) => e.address).toList(),
|
dstAddr: dstAddrs,
|
||||||
isSweepAll: false,
|
isSweepAll: false,
|
||||||
amounts: outputs.map((e) => monero.Wallet_amountFromString(e.amount)).toList(),
|
amounts: amounts,
|
||||||
mixinCount: 0,
|
mixinCount: 0,
|
||||||
pendingTransactionPriority: priorityRaw,
|
pendingTransactionPriority: priorityRaw,
|
||||||
subaddr_account: accountIndex,
|
subaddr_account: accountIndex,
|
||||||
|
@ -307,7 +313,34 @@ class Transaction {
|
||||||
confirmations = monero.TransactionInfo_confirmations(txInfo),
|
confirmations = monero.TransactionInfo_confirmations(txInfo),
|
||||||
fee = monero.TransactionInfo_fee(txInfo),
|
fee = monero.TransactionInfo_fee(txInfo),
|
||||||
description = monero.TransactionInfo_description(txInfo),
|
description = monero.TransactionInfo_description(txInfo),
|
||||||
key = monero.Wallet_getTxKey(wptr!, txid: monero.TransactionInfo_hash(txInfo));
|
key = getTxKey(txInfo);
|
||||||
|
|
||||||
|
static String getTxKey(monero.TransactionInfo txInfo) {
|
||||||
|
final txKey = monero.Wallet_getTxKey(wptr!, txid: monero.TransactionInfo_hash(txInfo));
|
||||||
|
final status = monero.Wallet_status(wptr!);
|
||||||
|
if (status != 0) {
|
||||||
|
return monero.Wallet_errorString(wptr!);
|
||||||
|
}
|
||||||
|
return breakTxKey(txKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
static String breakTxKey(String input) {
|
||||||
|
final x = 64;
|
||||||
|
StringBuffer buffer = StringBuffer();
|
||||||
|
|
||||||
|
for (int i = 0; i < input.length; i += x) {
|
||||||
|
int endIndex = i + x;
|
||||||
|
if (endIndex > input.length) {
|
||||||
|
endIndex = input.length;
|
||||||
|
}
|
||||||
|
buffer.write(input.substring(i, endIndex));
|
||||||
|
if (endIndex != input.length) {
|
||||||
|
buffer.write('\n\n');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return buffer.toString().trim();
|
||||||
|
}
|
||||||
|
|
||||||
Transaction.dummy({
|
Transaction.dummy({
|
||||||
required this.displayLabel,
|
required this.displayLabel,
|
||||||
|
|
|
@ -463,8 +463,8 @@ packages:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
path: "impls/monero.dart"
|
path: "impls/monero.dart"
|
||||||
ref: bcb328a4956105dc182afd0ce2e48fe263f5f20b
|
ref: 3cb38bee9385faf46b03fd73aab85f3ac4115bf7
|
||||||
resolved-ref: bcb328a4956105dc182afd0ce2e48fe263f5f20b
|
resolved-ref: 3cb38bee9385faf46b03fd73aab85f3ac4115bf7
|
||||||
url: "https://github.com/mrcyjanek/monero_c"
|
url: "https://github.com/mrcyjanek/monero_c"
|
||||||
source: git
|
source: git
|
||||||
version: "0.0.0"
|
version: "0.0.0"
|
||||||
|
|
|
@ -25,7 +25,7 @@ dependencies:
|
||||||
monero:
|
monero:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/mrcyjanek/monero_c
|
url: https://github.com/mrcyjanek/monero_c
|
||||||
ref: bcb328a4956105dc182afd0ce2e48fe263f5f20b # monero_c hash
|
ref: 3cb38bee9385faf46b03fd73aab85f3ac4115bf7 # monero_c hash
|
||||||
path: impls/monero.dart
|
path: impls/monero.dart
|
||||||
mutex: ^3.1.0
|
mutex: ^3.1.0
|
||||||
|
|
||||||
|
|
|
@ -466,21 +466,25 @@ class NanoClient {
|
||||||
|
|
||||||
blocks = blocks as Map<String, dynamic>;
|
blocks = blocks as Map<String, dynamic>;
|
||||||
|
|
||||||
// confirm all receivable blocks:
|
try {
|
||||||
for (final blockHash in blocks.keys) {
|
// confirm all receivable blocks:
|
||||||
final block = blocks[blockHash];
|
for (final blockHash in blocks.keys) {
|
||||||
final String amountRaw = block["amount"] as String;
|
final block = blocks[blockHash];
|
||||||
await receiveBlock(
|
final String amountRaw = block["amount"] as String;
|
||||||
blockHash: blockHash,
|
await receiveBlock(
|
||||||
amountRaw: amountRaw,
|
blockHash: blockHash,
|
||||||
privateKey: privateKey,
|
amountRaw: amountRaw,
|
||||||
destinationAddress: destinationAddress,
|
privateKey: privateKey,
|
||||||
);
|
destinationAddress: destinationAddress,
|
||||||
// a bit of a hack:
|
);
|
||||||
await Future<void>.delayed(const Duration(seconds: 2));
|
// a bit of a hack:
|
||||||
|
await Future<void>.delayed(const Duration(seconds: 2));
|
||||||
|
}
|
||||||
|
return blocks.keys.length;
|
||||||
|
} catch (_) {
|
||||||
|
// we failed to confirm all receivable blocks for w/e reason (PoW / node outage / etc)
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return blocks.keys.length;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void stop() {}
|
void stop() {}
|
||||||
|
|
|
@ -14,8 +14,11 @@ import 'package:bip39/bip39.dart' as bip39;
|
||||||
import 'package:nanodart/nanodart.dart';
|
import 'package:nanodart/nanodart.dart';
|
||||||
import 'package:nanoutil/nanoutil.dart';
|
import 'package:nanoutil/nanoutil.dart';
|
||||||
|
|
||||||
class NanoWalletService extends WalletService<NanoNewWalletCredentials,
|
class NanoWalletService extends WalletService<
|
||||||
NanoRestoreWalletFromSeedCredentials, NanoRestoreWalletFromKeysCredentials, NanoNewWalletCredentials> {
|
NanoNewWalletCredentials,
|
||||||
|
NanoRestoreWalletFromSeedCredentials,
|
||||||
|
NanoRestoreWalletFromKeysCredentials,
|
||||||
|
NanoNewWalletCredentials> {
|
||||||
NanoWalletService(this.walletInfoSource, this.isDirect);
|
NanoWalletService(this.walletInfoSource, this.isDirect);
|
||||||
|
|
||||||
final Box<WalletInfo> walletInfoSource;
|
final Box<WalletInfo> walletInfoSource;
|
||||||
|
@ -33,8 +36,12 @@ class NanoWalletService extends WalletService<NanoNewWalletCredentials,
|
||||||
String seedKey = NanoSeeds.generateSeed();
|
String seedKey = NanoSeeds.generateSeed();
|
||||||
String mnemonic = NanoDerivations.standardSeedToMnemonic(seedKey);
|
String mnemonic = NanoDerivations.standardSeedToMnemonic(seedKey);
|
||||||
|
|
||||||
// ensure default if not present:
|
// should never happen but just in case:
|
||||||
credentials.walletInfo!.derivationInfo ??= DerivationInfo(derivationType: DerivationType.nano);
|
if (credentials.walletInfo!.derivationInfo == null) {
|
||||||
|
credentials.walletInfo!.derivationInfo = DerivationInfo(derivationType: DerivationType.nano);
|
||||||
|
} else if (credentials.walletInfo!.derivationInfo!.derivationType == null) {
|
||||||
|
credentials.walletInfo!.derivationInfo!.derivationType = DerivationType.nano;
|
||||||
|
}
|
||||||
|
|
||||||
final wallet = NanoWallet(
|
final wallet = NanoWallet(
|
||||||
walletInfo: credentials.walletInfo!,
|
walletInfo: credentials.walletInfo!,
|
||||||
|
@ -86,7 +93,8 @@ class NanoWalletService extends WalletService<NanoNewWalletCredentials,
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<NanoWallet> restoreFromKeys(NanoRestoreWalletFromKeysCredentials credentials, {bool? isTestnet}) async {
|
Future<NanoWallet> restoreFromKeys(NanoRestoreWalletFromKeysCredentials credentials,
|
||||||
|
{bool? isTestnet}) async {
|
||||||
if (credentials.seedKey.contains(' ')) {
|
if (credentials.seedKey.contains(' ')) {
|
||||||
throw Exception("Invalid key!");
|
throw Exception("Invalid key!");
|
||||||
} else {
|
} else {
|
||||||
|
@ -106,6 +114,13 @@ class NanoWalletService extends WalletService<NanoNewWalletCredentials,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// should never happen but just in case:
|
||||||
|
if (credentials.walletInfo!.derivationInfo == null) {
|
||||||
|
credentials.walletInfo!.derivationInfo = DerivationInfo(derivationType: DerivationType.nano);
|
||||||
|
} else if (credentials.walletInfo!.derivationInfo!.derivationType == null) {
|
||||||
|
credentials.walletInfo!.derivationInfo!.derivationType = DerivationType.nano;
|
||||||
|
}
|
||||||
|
|
||||||
final wallet = await NanoWallet(
|
final wallet = await NanoWallet(
|
||||||
password: credentials.password!,
|
password: credentials.password!,
|
||||||
mnemonic: mnemonic ?? credentials.seedKey,
|
mnemonic: mnemonic ?? credentials.seedKey,
|
||||||
|
@ -119,11 +134,13 @@ class NanoWalletService extends WalletService<NanoNewWalletCredentials,
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<NanoWallet> restoreFromHardwareWallet(NanoNewWalletCredentials credentials) {
|
Future<NanoWallet> restoreFromHardwareWallet(NanoNewWalletCredentials credentials) {
|
||||||
throw UnimplementedError("Restoring a Nano wallet from a hardware wallet is not yet supported!");
|
throw UnimplementedError(
|
||||||
|
"Restoring a Nano wallet from a hardware wallet is not yet supported!");
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<NanoWallet> restoreFromSeed(NanoRestoreWalletFromSeedCredentials credentials, {bool? isTestnet}) async {
|
Future<NanoWallet> restoreFromSeed(NanoRestoreWalletFromSeedCredentials credentials,
|
||||||
|
{bool? isTestnet}) async {
|
||||||
if (credentials.mnemonic.contains(' ')) {
|
if (credentials.mnemonic.contains(' ')) {
|
||||||
if (!bip39.validateMnemonic(credentials.mnemonic)) {
|
if (!bip39.validateMnemonic(credentials.mnemonic)) {
|
||||||
throw nm.NanoMnemonicIsIncorrectException();
|
throw nm.NanoMnemonicIsIncorrectException();
|
||||||
|
|
|
@ -463,8 +463,8 @@ packages:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
path: "impls/monero.dart"
|
path: "impls/monero.dart"
|
||||||
ref: bcb328a4956105dc182afd0ce2e48fe263f5f20b
|
ref: 3cb38bee9385faf46b03fd73aab85f3ac4115bf7
|
||||||
resolved-ref: bcb328a4956105dc182afd0ce2e48fe263f5f20b
|
resolved-ref: 3cb38bee9385faf46b03fd73aab85f3ac4115bf7
|
||||||
url: "https://github.com/mrcyjanek/monero_c"
|
url: "https://github.com/mrcyjanek/monero_c"
|
||||||
source: git
|
source: git
|
||||||
version: "0.0.0"
|
version: "0.0.0"
|
||||||
|
|
|
@ -25,7 +25,7 @@ dependencies:
|
||||||
monero:
|
monero:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/mrcyjanek/monero_c
|
url: https://github.com/mrcyjanek/monero_c
|
||||||
ref: bcb328a4956105dc182afd0ce2e48fe263f5f20b # monero_c hash
|
ref: 3cb38bee9385faf46b03fd73aab85f3ac4115bf7 # monero_c hash
|
||||||
path: impls/monero.dart
|
path: impls/monero.dart
|
||||||
mutex: ^3.1.0
|
mutex: ^3.1.0
|
||||||
|
|
||||||
|
|
|
@ -40,6 +40,7 @@ const solanaDefaultNodeUri = 'rpc.ankr.com';
|
||||||
const tronDefaultNodeUri = 'trx.nownodes.io';
|
const tronDefaultNodeUri = 'trx.nownodes.io';
|
||||||
const newCakeWalletBitcoinUri = 'btc-electrum.cakewallet.com:50002';
|
const newCakeWalletBitcoinUri = 'btc-electrum.cakewallet.com:50002';
|
||||||
const wowneroDefaultNodeUri = 'node3.monerodevs.org:34568';
|
const wowneroDefaultNodeUri = 'node3.monerodevs.org:34568';
|
||||||
|
const moneroWorldNodeUri = '.moneroworld.com';
|
||||||
|
|
||||||
Future<void> defaultSettingsMigration(
|
Future<void> defaultSettingsMigration(
|
||||||
{required int version,
|
{required int version,
|
||||||
|
@ -245,6 +246,9 @@ Future<void> defaultSettingsMigration(
|
||||||
_fixNodesUseSSLFlag(nodes);
|
_fixNodesUseSSLFlag(nodes);
|
||||||
await changeDefaultNanoNode(nodes, sharedPreferences);
|
await changeDefaultNanoNode(nodes, sharedPreferences);
|
||||||
break;
|
break;
|
||||||
|
case 40:
|
||||||
|
await removeMoneroWorld(sharedPreferences: sharedPreferences, nodes: nodes);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -488,15 +492,7 @@ Node? getBitcoinCashDefaultElectrumServer({required Box<Node> nodes}) {
|
||||||
|
|
||||||
Node getMoneroDefaultNode({required Box<Node> nodes}) {
|
Node getMoneroDefaultNode({required Box<Node> nodes}) {
|
||||||
final timeZone = DateTime.now().timeZoneOffset.inHours;
|
final timeZone = DateTime.now().timeZoneOffset.inHours;
|
||||||
var nodeUri = '';
|
var nodeUri = newCakeWalletMoneroUri;
|
||||||
|
|
||||||
if (timeZone >= 1) {
|
|
||||||
// Eurasia
|
|
||||||
nodeUri = 'xmr-node-eu.cakewallet.com:18081';
|
|
||||||
} else if (timeZone <= -4) {
|
|
||||||
// America
|
|
||||||
nodeUri = 'xmr-node-usa-east.cakewallet.com:18081';
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return nodes.values.firstWhere((Node node) => node.uriRaw == nodeUri);
|
return nodes.values.firstWhere((Node node) => node.uriRaw == nodeUri);
|
||||||
|
@ -1260,3 +1256,22 @@ Future<void> replaceTronDefaultNode({
|
||||||
// If it's not, we switch user to the new default node: NowNodes
|
// If it's not, we switch user to the new default node: NowNodes
|
||||||
await changeTronCurrentNodeToDefault(sharedPreferences: sharedPreferences, nodes: nodes);
|
await changeTronCurrentNodeToDefault(sharedPreferences: sharedPreferences, nodes: nodes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> removeMoneroWorld(
|
||||||
|
{required SharedPreferences sharedPreferences, required Box<Node> nodes}) async {
|
||||||
|
const cakeWalletMoneroNodeUriPattern = '.moneroworld.com';
|
||||||
|
final currentMoneroNodeId = sharedPreferences.getInt(PreferencesKey.currentNodeIdKey);
|
||||||
|
final currentMoneroNode = nodes.values.firstWhere((node) => node.key == currentMoneroNodeId);
|
||||||
|
final needToReplaceCurrentMoneroNode = currentMoneroNode.uri.toString().contains(cakeWalletMoneroNodeUriPattern);
|
||||||
|
|
||||||
|
nodes.values.forEach((node) async {
|
||||||
|
if (node.type == WalletType.monero &&
|
||||||
|
node.uri.toString().contains(cakeWalletMoneroNodeUriPattern)) {
|
||||||
|
await node.delete();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (needToReplaceCurrentMoneroNode) {
|
||||||
|
await changeMoneroCurrentNodeToDefault(sharedPreferences: sharedPreferences, nodes: nodes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -27,6 +27,8 @@ class ExchangeProviderDescription extends EnumerableItem<int> with Serializable<
|
||||||
ExchangeProviderDescription(title: 'ThorChain', raw: 8, image: 'assets/images/thorchain.png');
|
ExchangeProviderDescription(title: 'ThorChain', raw: 8, image: 'assets/images/thorchain.png');
|
||||||
static const quantex =
|
static const quantex =
|
||||||
ExchangeProviderDescription(title: 'Quantex', raw: 9, image: 'assets/images/quantex.png');
|
ExchangeProviderDescription(title: 'Quantex', raw: 9, image: 'assets/images/quantex.png');
|
||||||
|
static const stealthEx =
|
||||||
|
ExchangeProviderDescription(title: 'StealthEx', raw: 10, image: 'assets/images/stealthex.png');
|
||||||
|
|
||||||
static ExchangeProviderDescription deserialize({required int raw}) {
|
static ExchangeProviderDescription deserialize({required int raw}) {
|
||||||
switch (raw) {
|
switch (raw) {
|
||||||
|
@ -50,6 +52,8 @@ class ExchangeProviderDescription extends EnumerableItem<int> with Serializable<
|
||||||
return thorChain;
|
return thorChain;
|
||||||
case 9:
|
case 9:
|
||||||
return quantex;
|
return quantex;
|
||||||
|
case 10:
|
||||||
|
return stealthEx;
|
||||||
default:
|
default:
|
||||||
throw Exception('Unexpected token: $raw for ExchangeProviderDescription deserialize');
|
throw Exception('Unexpected token: $raw for ExchangeProviderDescription deserialize');
|
||||||
}
|
}
|
||||||
|
|
299
lib/exchange/provider/stealth_ex_exchange_provider.dart
Normal file
299
lib/exchange/provider/stealth_ex_exchange_provider.dart
Normal file
|
@ -0,0 +1,299 @@
|
||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:developer';
|
||||||
|
|
||||||
|
import 'package:cake_wallet/.secrets.g.dart' as secrets;
|
||||||
|
import 'package:cake_wallet/exchange/provider/exchange_provider.dart';
|
||||||
|
import 'package:cake_wallet/exchange/exchange_provider_description.dart';
|
||||||
|
import 'package:cake_wallet/exchange/limits.dart';
|
||||||
|
import 'package:cake_wallet/exchange/trade.dart';
|
||||||
|
import 'package:cake_wallet/exchange/trade_not_created_exception.dart';
|
||||||
|
import 'package:cake_wallet/exchange/trade_request.dart';
|
||||||
|
import 'package:cake_wallet/exchange/trade_state.dart';
|
||||||
|
import 'package:cake_wallet/exchange/utils/currency_pairs_utils.dart';
|
||||||
|
import 'package:cw_core/crypto_currency.dart';
|
||||||
|
import 'package:http/http.dart' as http;
|
||||||
|
|
||||||
|
class StealthExExchangeProvider extends ExchangeProvider {
|
||||||
|
StealthExExchangeProvider() : super(pairList: supportedPairs(_notSupported));
|
||||||
|
|
||||||
|
static const List<CryptoCurrency> _notSupported = [];
|
||||||
|
|
||||||
|
static final apiKey = secrets.stealthExBearerToken;
|
||||||
|
static final _additionalFeePercent = double.tryParse(secrets.stealthExAdditionalFeePercent);
|
||||||
|
static const _baseUrl = 'https://api.stealthex.io';
|
||||||
|
static const _rangePath = '/v4/rates/range';
|
||||||
|
static const _amountPath = '/v4/rates/estimated-amount';
|
||||||
|
static const _exchangesPath = '/v4/exchanges';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get title => 'StealthEX';
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool get isAvailable => true;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool get isEnabled => true;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool get supportsFixedRate => true;
|
||||||
|
|
||||||
|
@override
|
||||||
|
ExchangeProviderDescription get description => ExchangeProviderDescription.stealthEx;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<bool> checkIsAvailable() async => true;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Limits> fetchLimits(
|
||||||
|
{required CryptoCurrency from,
|
||||||
|
required CryptoCurrency to,
|
||||||
|
required bool isFixedRateMode}) async {
|
||||||
|
final curFrom = isFixedRateMode ? to : from;
|
||||||
|
final curTo = isFixedRateMode ? from : to;
|
||||||
|
|
||||||
|
final headers = {'Authorization': apiKey, 'Content-Type': 'application/json'};
|
||||||
|
final body = {
|
||||||
|
'route': {
|
||||||
|
'from': {'symbol': _getName(curFrom), 'network': _getNetwork(curFrom)},
|
||||||
|
'to': {'symbol': _getName(curTo), 'network': _getNetwork(curTo)}
|
||||||
|
},
|
||||||
|
'estimation': isFixedRateMode ? 'reversed' : 'direct',
|
||||||
|
'rate': isFixedRateMode ? 'fixed' : 'floating',
|
||||||
|
'additional_fee_percent': _additionalFeePercent,
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
final response = await http.post(Uri.parse(_baseUrl + _rangePath),
|
||||||
|
headers: headers, body: json.encode(body));
|
||||||
|
if (response.statusCode != 200) {
|
||||||
|
throw Exception('StealthEx fetch limits failed: ${response.body}');
|
||||||
|
}
|
||||||
|
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||||
|
final min = responseJSON['min_amount'] as double?;
|
||||||
|
final max = responseJSON['max_amount'] as double?;
|
||||||
|
return Limits(min: min, max: max);
|
||||||
|
} catch (e) {
|
||||||
|
log(e.toString());
|
||||||
|
throw Exception('StealthEx failed to fetch limits');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<double> fetchRate(
|
||||||
|
{required CryptoCurrency from,
|
||||||
|
required CryptoCurrency to,
|
||||||
|
required double amount,
|
||||||
|
required bool isFixedRateMode,
|
||||||
|
required bool isReceiveAmount}) async {
|
||||||
|
final response = await getEstimatedExchangeAmount(
|
||||||
|
from: from, to: to, amount: amount, isFixedRateMode: isFixedRateMode);
|
||||||
|
final estimatedAmount = response['estimated_amount'] as double? ?? 0.0;
|
||||||
|
return estimatedAmount > 0.0
|
||||||
|
? isFixedRateMode
|
||||||
|
? amount / estimatedAmount
|
||||||
|
: estimatedAmount / amount
|
||||||
|
: 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Trade> createTrade(
|
||||||
|
{required TradeRequest request,
|
||||||
|
required bool isFixedRateMode,
|
||||||
|
required bool isSendAll}) async {
|
||||||
|
String? rateId;
|
||||||
|
String? validUntil;
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (isFixedRateMode) {
|
||||||
|
final response = await getEstimatedExchangeAmount(
|
||||||
|
from: request.fromCurrency,
|
||||||
|
to: request.toCurrency,
|
||||||
|
amount: double.parse(request.toAmount),
|
||||||
|
isFixedRateMode: isFixedRateMode);
|
||||||
|
rateId = response['rate_id'] as String?;
|
||||||
|
validUntil = response['valid_until'] as String?;
|
||||||
|
if (rateId == null) throw TradeNotCreatedException(description);
|
||||||
|
}
|
||||||
|
|
||||||
|
final headers = {'Authorization': apiKey, 'Content-Type': 'application/json'};
|
||||||
|
final body = {
|
||||||
|
'route': {
|
||||||
|
'from': {
|
||||||
|
'symbol': _getName(request.fromCurrency),
|
||||||
|
'network': _getNetwork(request.fromCurrency)
|
||||||
|
},
|
||||||
|
'to': {'symbol': _getName(request.toCurrency), 'network': _getNetwork(request.toCurrency)}
|
||||||
|
},
|
||||||
|
'estimation': isFixedRateMode ? 'reversed' : 'direct',
|
||||||
|
'rate': isFixedRateMode ? 'fixed' : 'floating',
|
||||||
|
if (isFixedRateMode) 'rate_id': rateId,
|
||||||
|
'amount':
|
||||||
|
isFixedRateMode ? double.parse(request.toAmount) : double.parse(request.fromAmount),
|
||||||
|
'address': request.toAddress,
|
||||||
|
'refund_address': request.refundAddress,
|
||||||
|
'additional_fee_percent': _additionalFeePercent,
|
||||||
|
};
|
||||||
|
|
||||||
|
final response = await http.post(Uri.parse(_baseUrl + _exchangesPath),
|
||||||
|
headers: headers, body: json.encode(body));
|
||||||
|
|
||||||
|
if (response.statusCode != 201) {
|
||||||
|
throw Exception('StealthEx create trade failed: ${response.body}');
|
||||||
|
}
|
||||||
|
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||||
|
final deposit = responseJSON['deposit'] as Map<String, dynamic>;
|
||||||
|
final withdrawal = responseJSON['withdrawal'] as Map<String, dynamic>;
|
||||||
|
|
||||||
|
final id = responseJSON['id'] as String;
|
||||||
|
final from = deposit['symbol'] as String;
|
||||||
|
final to = withdrawal['symbol'] as String;
|
||||||
|
final payoutAddress = withdrawal['address'] as String;
|
||||||
|
final depositAddress = deposit['address'] as String;
|
||||||
|
final refundAddress = responseJSON['refund_address'] as String;
|
||||||
|
final depositAmount = toDouble(deposit['amount']);
|
||||||
|
final receiveAmount = toDouble(withdrawal['amount']);
|
||||||
|
final status = responseJSON['status'] as String;
|
||||||
|
final createdAtString = responseJSON['created_at'] as String;
|
||||||
|
|
||||||
|
final createdAt = DateTime.parse(createdAtString);
|
||||||
|
final expiredAt = validUntil != null
|
||||||
|
? DateTime.parse(validUntil)
|
||||||
|
: DateTime.now().add(Duration(minutes: 5));
|
||||||
|
|
||||||
|
|
||||||
|
CryptoCurrency fromCurrency;
|
||||||
|
if (request.fromCurrency.tag != null && request.fromCurrency.title.toLowerCase() == from) {
|
||||||
|
fromCurrency = request.fromCurrency;
|
||||||
|
} else {
|
||||||
|
fromCurrency = CryptoCurrency.fromString(from);
|
||||||
|
}
|
||||||
|
|
||||||
|
CryptoCurrency toCurrency;
|
||||||
|
if (request.toCurrency.tag != null && request.toCurrency.title.toLowerCase() == to) {
|
||||||
|
toCurrency = request.toCurrency;
|
||||||
|
} else {
|
||||||
|
toCurrency = CryptoCurrency.fromString(to);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Trade(
|
||||||
|
id: id,
|
||||||
|
from: fromCurrency,
|
||||||
|
to: toCurrency,
|
||||||
|
provider: description,
|
||||||
|
inputAddress: depositAddress,
|
||||||
|
payoutAddress: payoutAddress,
|
||||||
|
refundAddress: refundAddress,
|
||||||
|
amount: depositAmount.toString(),
|
||||||
|
receiveAmount: receiveAmount.toString(),
|
||||||
|
state: TradeState.deserialize(raw: status),
|
||||||
|
createdAt: createdAt,
|
||||||
|
expiredAt: expiredAt,
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
log(e.toString());
|
||||||
|
throw TradeNotCreatedException(description);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Trade> findTradeById({required String id}) async {
|
||||||
|
final headers = {'Authorization': apiKey, 'Content-Type': 'application/json'};
|
||||||
|
|
||||||
|
final uri = Uri.parse('$_baseUrl$_exchangesPath/$id');
|
||||||
|
final response = await http.get(uri, headers: headers);
|
||||||
|
|
||||||
|
if (response.statusCode != 200) {
|
||||||
|
throw Exception('StealthEx fetch trade failed: ${response.body}');
|
||||||
|
}
|
||||||
|
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||||
|
final deposit = responseJSON['deposit'] as Map<String, dynamic>;
|
||||||
|
final withdrawal = responseJSON['withdrawal'] as Map<String, dynamic>;
|
||||||
|
|
||||||
|
final respId = responseJSON['id'] as String;
|
||||||
|
final from = deposit['symbol'] as String;
|
||||||
|
final to = withdrawal['symbol'] as String;
|
||||||
|
final payoutAddress = withdrawal['address'] as String;
|
||||||
|
final depositAddress = deposit['address'] as String;
|
||||||
|
final refundAddress = responseJSON['refund_address'] as String;
|
||||||
|
final depositAmount = toDouble(deposit['amount']);
|
||||||
|
final receiveAmount = toDouble(withdrawal['amount']);
|
||||||
|
final status = responseJSON['status'] as String;
|
||||||
|
final createdAtString = responseJSON['created_at'] as String;
|
||||||
|
final createdAt = DateTime.parse(createdAtString);
|
||||||
|
|
||||||
|
return Trade(
|
||||||
|
id: respId,
|
||||||
|
from: CryptoCurrency.fromString(from),
|
||||||
|
to: CryptoCurrency.fromString(to),
|
||||||
|
provider: description,
|
||||||
|
inputAddress: depositAddress,
|
||||||
|
payoutAddress: payoutAddress,
|
||||||
|
refundAddress: refundAddress,
|
||||||
|
amount: depositAmount.toString(),
|
||||||
|
receiveAmount: receiveAmount.toString(),
|
||||||
|
state: TradeState.deserialize(raw: status),
|
||||||
|
createdAt: createdAt,
|
||||||
|
isRefund: status == 'refunded',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Map<String, dynamic>> getEstimatedExchangeAmount(
|
||||||
|
{required CryptoCurrency from,
|
||||||
|
required CryptoCurrency to,
|
||||||
|
required double amount,
|
||||||
|
required bool isFixedRateMode}) async {
|
||||||
|
final headers = {'Authorization': apiKey, 'Content-Type': 'application/json'};
|
||||||
|
|
||||||
|
final body = {
|
||||||
|
'route': {
|
||||||
|
'from': {'symbol': _getName(from), 'network': _getNetwork(from)},
|
||||||
|
'to': {'symbol': _getName(to), 'network': _getNetwork(to)}
|
||||||
|
},
|
||||||
|
'estimation': isFixedRateMode ? 'reversed' : 'direct',
|
||||||
|
'rate': isFixedRateMode ? 'fixed' : 'floating',
|
||||||
|
'amount': amount,
|
||||||
|
'additional_fee_percent': _additionalFeePercent,
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
final response = await http.post(Uri.parse(_baseUrl + _amountPath),
|
||||||
|
headers: headers, body: json.encode(body));
|
||||||
|
if (response.statusCode != 200) return {};
|
||||||
|
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||||
|
final rate = responseJSON['rate'] as Map<String, dynamic>?;
|
||||||
|
return {
|
||||||
|
'estimated_amount': responseJSON['estimated_amount'] as double?,
|
||||||
|
if (rate != null) 'valid_until': rate['valid_until'] as String?,
|
||||||
|
if (rate != null) 'rate_id': rate['id'] as String?
|
||||||
|
};
|
||||||
|
} catch (e) {
|
||||||
|
log(e.toString());
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
double toDouble(dynamic value) {
|
||||||
|
if (value is int) {
|
||||||
|
return value.toDouble();
|
||||||
|
} else if (value is double) {
|
||||||
|
return value;
|
||||||
|
} else {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String _getName(CryptoCurrency currency) {
|
||||||
|
if (currency == CryptoCurrency.usdcEPoly) return 'usdce';
|
||||||
|
return currency.title.toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
String _getNetwork(CryptoCurrency currency) {
|
||||||
|
if (currency.tag == null) return 'mainnet';
|
||||||
|
|
||||||
|
if (currency == CryptoCurrency.maticpoly) return 'mainnet';
|
||||||
|
|
||||||
|
if (currency.tag == 'POLY') return 'matic';
|
||||||
|
|
||||||
|
return currency.tag!.toLowerCase();
|
||||||
|
}
|
||||||
|
}
|
|
@ -40,7 +40,6 @@ class TradeState extends EnumerableItem<String> with Serializable<String> {
|
||||||
static const exchanging = TradeState(raw: 'exchanging', title: 'Exchanging');
|
static const exchanging = TradeState(raw: 'exchanging', title: 'Exchanging');
|
||||||
static const sending = TradeState(raw: 'sending', title: 'Sending');
|
static const sending = TradeState(raw: 'sending', title: 'Sending');
|
||||||
static const success = TradeState(raw: 'success', title: 'Success');
|
static const success = TradeState(raw: 'success', title: 'Success');
|
||||||
|
|
||||||
static TradeState deserialize({required String raw}) {
|
static TradeState deserialize({required String raw}) {
|
||||||
|
|
||||||
switch (raw) {
|
switch (raw) {
|
||||||
|
@ -119,6 +118,7 @@ class TradeState extends EnumerableItem<String> with Serializable<String> {
|
||||||
case 'refunded':
|
case 'refunded':
|
||||||
return refunded;
|
return refunded;
|
||||||
case 'confirmation':
|
case 'confirmation':
|
||||||
|
case 'verifying':
|
||||||
return confirmation;
|
return confirmation;
|
||||||
case 'confirmed':
|
case 'confirmed':
|
||||||
return confirmed;
|
return confirmed;
|
||||||
|
|
|
@ -49,11 +49,14 @@ final rootKey = GlobalKey<RootState>();
|
||||||
final RouteObserver<PageRoute<dynamic>> routeObserver = RouteObserver<PageRoute<dynamic>>();
|
final RouteObserver<PageRoute<dynamic>> routeObserver = RouteObserver<PageRoute<dynamic>>();
|
||||||
|
|
||||||
Future<void> main() async {
|
Future<void> main() async {
|
||||||
|
await runAppWithZone();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> runAppWithZone() async {
|
||||||
bool isAppRunning = false;
|
bool isAppRunning = false;
|
||||||
|
|
||||||
await runZonedGuarded(() async {
|
await runZonedGuarded(() async {
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
|
|
||||||
FlutterError.onError = ExceptionHandler.onError;
|
FlutterError.onError = ExceptionHandler.onError;
|
||||||
|
|
||||||
/// A callback that is invoked when an unhandled error occurs in the root
|
/// A callback that is invoked when an unhandled error occurs in the root
|
||||||
|
@ -63,42 +66,14 @@ Future<void> main() async {
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
await initializeAppAtRoot();
|
||||||
await setDefaultMinimumWindowSize();
|
|
||||||
|
|
||||||
await CakeHive.close();
|
|
||||||
|
|
||||||
await initializeAppConfigs();
|
|
||||||
|
|
||||||
runApp(App());
|
runApp(App());
|
||||||
|
|
||||||
isAppRunning = true;
|
isAppRunning = true;
|
||||||
}, (error, stackTrace) async {
|
}, (error, stackTrace) async {
|
||||||
if (!isAppRunning) {
|
if (!isAppRunning) {
|
||||||
runApp(
|
runApp(
|
||||||
MaterialApp(
|
TopLevelErrorWidget(error: error, stackTrace: stackTrace),
|
||||||
debugShowCheckedModeBanner: false,
|
|
||||||
scrollBehavior: AppScrollBehavior(),
|
|
||||||
home: Scaffold(
|
|
||||||
body: SingleChildScrollView(
|
|
||||||
child: Container(
|
|
||||||
margin: EdgeInsets.only(top: 50, left: 20, right: 20, bottom: 20),
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
'Error:\n${error.toString()}',
|
|
||||||
style: TextStyle(fontSize: 22),
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
'Stack trace:\n${stackTrace.toString()}',
|
|
||||||
style: TextStyle(fontSize: 16),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,6 +81,12 @@ Future<void> main() async {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> initializeAppAtRoot({bool reInitializing = false}) async {
|
||||||
|
if (!reInitializing) await setDefaultMinimumWindowSize();
|
||||||
|
await CakeHive.close();
|
||||||
|
await initializeAppConfigs();
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> initializeAppConfigs() async {
|
Future<void> initializeAppConfigs() async {
|
||||||
setRootDirFromEnv();
|
setRootDirFromEnv();
|
||||||
final appDir = await getAppDir();
|
final appDir = await getAppDir();
|
||||||
|
@ -210,7 +191,7 @@ Future<void> initializeAppConfigs() async {
|
||||||
transactionDescriptions: transactionDescriptions,
|
transactionDescriptions: transactionDescriptions,
|
||||||
secureStorage: secureStorage,
|
secureStorage: secureStorage,
|
||||||
anonpayInvoiceInfo: anonpayInvoiceInfo,
|
anonpayInvoiceInfo: anonpayInvoiceInfo,
|
||||||
initialMigrationVersion: 39,
|
initialMigrationVersion: 40,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -342,3 +323,41 @@ class _HomeState extends State<_Home> {
|
||||||
return const SizedBox.shrink();
|
return const SizedBox.shrink();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class TopLevelErrorWidget extends StatelessWidget {
|
||||||
|
const TopLevelErrorWidget({
|
||||||
|
required this.error,
|
||||||
|
required this.stackTrace,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
final Object error;
|
||||||
|
final StackTrace stackTrace;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return MaterialApp(
|
||||||
|
debugShowCheckedModeBanner: false,
|
||||||
|
scrollBehavior: AppScrollBehavior(),
|
||||||
|
home: Scaffold(
|
||||||
|
body: SingleChildScrollView(
|
||||||
|
child: Container(
|
||||||
|
margin: EdgeInsets.only(top: 50, left: 20, right: 20, bottom: 20),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'Error:\n${error.toString()}',
|
||||||
|
style: TextStyle(fontSize: 22),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
'Stack trace:\n${stackTrace.toString()}',
|
||||||
|
style: TextStyle(fontSize: 16),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -40,13 +40,11 @@ class NanoChangeRepPage extends BasePage {
|
||||||
(node) => node.account == currentRepAccount,
|
(node) => node.account == currentRepAccount,
|
||||||
orElse: () => N2Node(
|
orElse: () => N2Node(
|
||||||
account: currentRepAccount,
|
account: currentRepAccount,
|
||||||
alias: currentRepAccount,
|
|
||||||
score: 0,
|
score: 0,
|
||||||
uptime: "???",
|
uptime: "???",
|
||||||
weight: 0,
|
weight: 0,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
return currentNode;
|
return currentNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,9 +55,7 @@ class NanoChangeRepPage extends BasePage {
|
||||||
child: FutureBuilder(
|
child: FutureBuilder(
|
||||||
future: nano!.getN2Reps(_wallet),
|
future: nano!.getN2Reps(_wallet),
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
if (snapshot.data == null) {
|
final reps = snapshot.data ?? [];
|
||||||
return SizedBox();
|
|
||||||
}
|
|
||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
padding: EdgeInsets.only(left: 24, right: 24),
|
padding: EdgeInsets.only(left: 24, right: 24),
|
||||||
|
@ -101,29 +97,35 @@ class NanoChangeRepPage extends BasePage {
|
||||||
),
|
),
|
||||||
_buildSingleRepresentative(
|
_buildSingleRepresentative(
|
||||||
context,
|
context,
|
||||||
getCurrentRepNode(snapshot.data as List<N2Node>),
|
getCurrentRepNode(reps),
|
||||||
isList: false,
|
isList: false,
|
||||||
|
divider: false,
|
||||||
),
|
),
|
||||||
Divider(height: 20),
|
if (reps.isNotEmpty) ...[
|
||||||
Container(
|
Divider(height: 20),
|
||||||
margin: EdgeInsets.only(top: 12),
|
Container(
|
||||||
child: Text(
|
margin: EdgeInsets.only(top: 12),
|
||||||
S.current.nano_pick_new_rep,
|
child: Text(
|
||||||
style: TextStyle(
|
S.current.nano_pick_new_rep,
|
||||||
fontSize: 16,
|
style: TextStyle(
|
||||||
fontWeight: FontWeight.w700,
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w700,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
Divider(height: 20),
|
||||||
|
],
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
contentPadding: EdgeInsets.only(bottom: 24),
|
contentPadding: EdgeInsets.only(bottom: 24),
|
||||||
content: Container(
|
content: Container(
|
||||||
child: Column(
|
child: reps.isNotEmpty
|
||||||
children: _getRepresentativeWidgets(context, snapshot.data as List<N2Node>),
|
? Column(
|
||||||
),
|
children: _getRepresentativeWidgets(context, reps),
|
||||||
|
)
|
||||||
|
: SizedBox(),
|
||||||
),
|
),
|
||||||
bottomSectionPadding: EdgeInsets.only(bottom: 24),
|
bottomSectionPadding: EdgeInsets.only(bottom: 24),
|
||||||
bottomSection: Observer(
|
bottomSection: Observer(
|
||||||
|
@ -207,19 +209,22 @@ class NanoChangeRepPage extends BasePage {
|
||||||
final List<Widget> ret = [];
|
final List<Widget> ret = [];
|
||||||
for (final N2Node node in list) {
|
for (final N2Node node in list) {
|
||||||
if (node.alias != null && node.alias!.trim().isNotEmpty) {
|
if (node.alias != null && node.alias!.trim().isNotEmpty) {
|
||||||
ret.add(_buildSingleRepresentative(context, node));
|
bool divider = node != list.first;
|
||||||
|
ret.add(_buildSingleRepresentative(context, node, divider: divider, isList: true));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildSingleRepresentative(BuildContext context, N2Node rep, {bool isList = true}) {
|
Widget _buildSingleRepresentative(
|
||||||
|
BuildContext context,
|
||||||
|
N2Node rep, {
|
||||||
|
bool isList = true,
|
||||||
|
bool divider = false,
|
||||||
|
}) {
|
||||||
return Column(
|
return Column(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
if (isList)
|
if (divider) Divider(height: 2),
|
||||||
Divider(
|
|
||||||
height: 2,
|
|
||||||
),
|
|
||||||
TextButton(
|
TextButton(
|
||||||
style: TextButton.styleFrom(
|
style: TextButton.styleFrom(
|
||||||
padding: EdgeInsets.zero,
|
padding: EdgeInsets.zero,
|
||||||
|
@ -244,11 +249,11 @@ class NanoChangeRepPage extends BasePage {
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Text(
|
Text(
|
||||||
_sanitizeAlias(rep.alias),
|
rep.alias ?? rep.account!,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
|
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
|
||||||
fontWeight: FontWeight.w700,
|
fontWeight: FontWeight.w700,
|
||||||
fontSize: 18,
|
fontSize: rep.alias == null ? 14 : 18,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Container(
|
Container(
|
||||||
|
@ -337,11 +342,4 @@ class NanoChangeRepPage extends BasePage {
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
String _sanitizeAlias(String? alias) {
|
|
||||||
if (alias != null) {
|
|
||||||
return alias.replaceAll(RegExp(r'[^a-zA-Z_.!?_;:-]'), '');
|
|
||||||
}
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -406,24 +406,16 @@ class WalletRestorePage extends BasePage {
|
||||||
) as DerivationInfo?;
|
) as DerivationInfo?;
|
||||||
} else if (derivationsWithHistory == 1) {
|
} else if (derivationsWithHistory == 1) {
|
||||||
dInfo = derivations[derivationWithHistoryIndex];
|
dInfo = derivations[derivationWithHistoryIndex];
|
||||||
}
|
} else if (derivations.length == 1) {
|
||||||
|
|
||||||
// get the default derivation for this wallet type:
|
|
||||||
if (dInfo == null) {
|
|
||||||
// we only return 1 derivation if we're pretty sure we know which one to use:
|
// we only return 1 derivation if we're pretty sure we know which one to use:
|
||||||
if (derivations.length == 1) {
|
dInfo = derivations.first;
|
||||||
dInfo = derivations.first;
|
} else {
|
||||||
} else {
|
// if we have multiple possible derivations, and none (or multiple) have histories
|
||||||
// if we have multiple possible derivations, and none have histories
|
// we just default to the most common one:
|
||||||
// we just default to the most common one:
|
dInfo = walletRestoreViewModel.getCommonRestoreDerivation();
|
||||||
dInfo = walletRestoreViewModel.getCommonRestoreDerivation();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.derivationInfo = dInfo;
|
this.derivationInfo = dInfo;
|
||||||
if (this.derivationInfo == null) {
|
|
||||||
this.derivationInfo = walletRestoreViewModel.getDefaultDerivation();
|
|
||||||
}
|
|
||||||
|
|
||||||
await walletRestoreViewModel.create(options: _credentials());
|
await walletRestoreViewModel.create(options: _credentials());
|
||||||
seedSettingsViewModel.setPassphrase(null);
|
seedSettingsViewModel.setPassphrase(null);
|
||||||
|
|
|
@ -273,6 +273,7 @@ class SendPage extends BasePage {
|
||||||
? template.cryptoCurrency
|
? template.cryptoCurrency
|
||||||
: template.fiatCurrency,
|
: template.fiatCurrency,
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
|
sendViewModel.state = IsExecutingState();
|
||||||
if (template.additionalRecipients?.isNotEmpty ?? false) {
|
if (template.additionalRecipients?.isNotEmpty ?? false) {
|
||||||
sendViewModel.clearOutputs();
|
sendViewModel.clearOutputs();
|
||||||
|
|
||||||
|
@ -301,6 +302,7 @@ class SendPage extends BasePage {
|
||||||
template: template,
|
template: template,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
sendViewModel.state = InitialExecutionState();
|
||||||
},
|
},
|
||||||
onRemove: () {
|
onRemove: () {
|
||||||
showPopUp<void>(
|
showPopUp<void>(
|
||||||
|
@ -368,6 +370,7 @@ class SendPage extends BasePage {
|
||||||
builder: (_) {
|
builder: (_) {
|
||||||
return LoadingPrimaryButton(
|
return LoadingPrimaryButton(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
|
if (sendViewModel.state is IsExecutingState) return;
|
||||||
if (_formKey.currentState != null && !_formKey.currentState!.validate()) {
|
if (_formKey.currentState != null && !_formKey.currentState!.validate()) {
|
||||||
if (sendViewModel.outputs.length > 1) {
|
if (sendViewModel.outputs.length > 1) {
|
||||||
showErrorValidationAlert(context);
|
showErrorValidationAlert(context);
|
||||||
|
|
|
@ -113,10 +113,6 @@ class CheckBoxPickerState extends State<CheckBoxPicker> {
|
||||||
|
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
if (item.isDisabled) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool newValue = !item.value;
|
bool newValue = !item.value;
|
||||||
item.value = newValue;
|
item.value = newValue;
|
||||||
widget.onChanged(index, newValue);
|
widget.onChanged(index, newValue);
|
||||||
|
@ -134,7 +130,7 @@ class CheckBoxPickerState extends State<CheckBoxPicker> {
|
||||||
borderColor: Theme.of(context).dividerColor,
|
borderColor: Theme.of(context).dividerColor,
|
||||||
iconColor: Colors.white,
|
iconColor: Colors.white,
|
||||||
onChanged: (bool? value) {
|
onChanged: (bool? value) {
|
||||||
if (value == null || item.isDisabled) {
|
if (value == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,8 @@ abstract class TradeFilterStoreBase with Store {
|
||||||
displaySimpleSwap = true,
|
displaySimpleSwap = true,
|
||||||
displayTrocador = true,
|
displayTrocador = true,
|
||||||
displayExolix = true,
|
displayExolix = true,
|
||||||
displayThorChain = true;
|
displayThorChain = true,
|
||||||
|
displayStealthEx = true;
|
||||||
|
|
||||||
@observable
|
@observable
|
||||||
bool displayXMRTO;
|
bool displayXMRTO;
|
||||||
|
@ -42,6 +43,9 @@ abstract class TradeFilterStoreBase with Store {
|
||||||
@observable
|
@observable
|
||||||
bool displayThorChain;
|
bool displayThorChain;
|
||||||
|
|
||||||
|
@observable
|
||||||
|
bool displayStealthEx;
|
||||||
|
|
||||||
@computed
|
@computed
|
||||||
bool get displayAllTrades =>
|
bool get displayAllTrades =>
|
||||||
displayChangeNow &&
|
displayChangeNow &&
|
||||||
|
@ -49,7 +53,8 @@ abstract class TradeFilterStoreBase with Store {
|
||||||
displaySimpleSwap &&
|
displaySimpleSwap &&
|
||||||
displayTrocador &&
|
displayTrocador &&
|
||||||
displayExolix &&
|
displayExolix &&
|
||||||
displayThorChain;
|
displayThorChain &&
|
||||||
|
displayStealthEx;
|
||||||
|
|
||||||
@action
|
@action
|
||||||
void toggleDisplayExchange(ExchangeProviderDescription provider) {
|
void toggleDisplayExchange(ExchangeProviderDescription provider) {
|
||||||
|
@ -78,6 +83,9 @@ abstract class TradeFilterStoreBase with Store {
|
||||||
case ExchangeProviderDescription.thorChain:
|
case ExchangeProviderDescription.thorChain:
|
||||||
displayThorChain = !displayThorChain;
|
displayThorChain = !displayThorChain;
|
||||||
break;
|
break;
|
||||||
|
case ExchangeProviderDescription.stealthEx:
|
||||||
|
displayStealthEx = !displayStealthEx;
|
||||||
|
break;
|
||||||
case ExchangeProviderDescription.all:
|
case ExchangeProviderDescription.all:
|
||||||
if (displayAllTrades) {
|
if (displayAllTrades) {
|
||||||
displayChangeNow = false;
|
displayChangeNow = false;
|
||||||
|
@ -88,6 +96,7 @@ abstract class TradeFilterStoreBase with Store {
|
||||||
displayTrocador = false;
|
displayTrocador = false;
|
||||||
displayExolix = false;
|
displayExolix = false;
|
||||||
displayThorChain = false;
|
displayThorChain = false;
|
||||||
|
displayStealthEx = false;
|
||||||
} else {
|
} else {
|
||||||
displayChangeNow = true;
|
displayChangeNow = true;
|
||||||
displaySideShift = true;
|
displaySideShift = true;
|
||||||
|
@ -97,6 +106,7 @@ abstract class TradeFilterStoreBase with Store {
|
||||||
displayTrocador = true;
|
displayTrocador = true;
|
||||||
displayExolix = true;
|
displayExolix = true;
|
||||||
displayThorChain = true;
|
displayThorChain = true;
|
||||||
|
displayStealthEx = true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -112,13 +122,19 @@ abstract class TradeFilterStoreBase with Store {
|
||||||
? _trades
|
? _trades
|
||||||
.where((item) =>
|
.where((item) =>
|
||||||
(displayXMRTO && item.trade.provider == ExchangeProviderDescription.xmrto) ||
|
(displayXMRTO && item.trade.provider == ExchangeProviderDescription.xmrto) ||
|
||||||
(displaySideShift && item.trade.provider == ExchangeProviderDescription.sideShift) ||
|
(displaySideShift &&
|
||||||
(displayChangeNow && item.trade.provider == ExchangeProviderDescription.changeNow) ||
|
item.trade.provider == ExchangeProviderDescription.sideShift) ||
|
||||||
(displayMorphToken && item.trade.provider == ExchangeProviderDescription.morphToken) ||
|
(displayChangeNow &&
|
||||||
(displaySimpleSwap && item.trade.provider == ExchangeProviderDescription.simpleSwap) ||
|
item.trade.provider == ExchangeProviderDescription.changeNow) ||
|
||||||
|
(displayMorphToken &&
|
||||||
|
item.trade.provider == ExchangeProviderDescription.morphToken) ||
|
||||||
|
(displaySimpleSwap &&
|
||||||
|
item.trade.provider == ExchangeProviderDescription.simpleSwap) ||
|
||||||
(displayTrocador && item.trade.provider == ExchangeProviderDescription.trocador) ||
|
(displayTrocador && item.trade.provider == ExchangeProviderDescription.trocador) ||
|
||||||
(displayExolix && item.trade.provider == ExchangeProviderDescription.exolix) ||
|
(displayExolix && item.trade.provider == ExchangeProviderDescription.exolix) ||
|
||||||
(displayThorChain && item.trade.provider == ExchangeProviderDescription.thorChain))
|
(displayThorChain &&
|
||||||
|
item.trade.provider == ExchangeProviderDescription.thorChain) ||
|
||||||
|
(displayStealthEx && item.trade.provider == ExchangeProviderDescription.stealthEx))
|
||||||
.toList()
|
.toList()
|
||||||
: _trades;
|
: _trades;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:cake_wallet/.secrets.g.dart' as secrets;
|
||||||
|
import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
||||||
import 'package:cake_wallet/buy/buy_provider.dart';
|
import 'package:cake_wallet/buy/buy_provider.dart';
|
||||||
import 'package:cake_wallet/core/key_service.dart';
|
import 'package:cake_wallet/core/key_service.dart';
|
||||||
import 'package:cake_wallet/entities/auto_generate_subaddress_status.dart';
|
import 'package:cake_wallet/entities/auto_generate_subaddress_status.dart';
|
||||||
import 'package:cake_wallet/entities/balance_display_mode.dart';
|
import 'package:cake_wallet/entities/balance_display_mode.dart';
|
||||||
|
import 'package:cake_wallet/entities/exchange_api_mode.dart';
|
||||||
import 'package:cake_wallet/entities/preferences_key.dart';
|
import 'package:cake_wallet/entities/preferences_key.dart';
|
||||||
import 'package:cake_wallet/entities/provider_types.dart';
|
import 'package:cake_wallet/entities/provider_types.dart';
|
||||||
import 'package:cake_wallet/entities/exchange_api_mode.dart';
|
|
||||||
import 'package:cake_wallet/entities/service_status.dart';
|
import 'package:cake_wallet/entities/service_status.dart';
|
||||||
import 'package:cake_wallet/exchange/exchange_provider_description.dart';
|
import 'package:cake_wallet/exchange/exchange_provider_description.dart';
|
||||||
import 'package:cake_wallet/generated/i18n.dart';
|
import 'package:cake_wallet/generated/i18n.dart';
|
||||||
|
@ -45,11 +47,9 @@ import 'package:cw_core/wallet_info.dart';
|
||||||
import 'package:cw_core/wallet_type.dart';
|
import 'package:cw_core/wallet_type.dart';
|
||||||
import 'package:eth_sig_util/util/utils.dart';
|
import 'package:eth_sig_util/util/utils.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:mobx/mobx.dart';
|
|
||||||
import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:http/http.dart' as http;
|
||||||
|
import 'package:mobx/mobx.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
import 'package:cake_wallet/.secrets.g.dart' as secrets;
|
|
||||||
|
|
||||||
part 'dashboard_view_model.g.dart';
|
part 'dashboard_view_model.g.dart';
|
||||||
|
|
||||||
|
@ -129,6 +129,11 @@ abstract class DashboardViewModelBase with Store {
|
||||||
caption: ExchangeProviderDescription.thorChain.title,
|
caption: ExchangeProviderDescription.thorChain.title,
|
||||||
onChanged: () =>
|
onChanged: () =>
|
||||||
tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.thorChain)),
|
tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.thorChain)),
|
||||||
|
FilterItem(
|
||||||
|
value: () => tradeFilterStore.displayStealthEx,
|
||||||
|
caption: ExchangeProviderDescription.stealthEx.title,
|
||||||
|
onChanged: () =>
|
||||||
|
tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.stealthEx)),
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
subname = '',
|
subname = '',
|
||||||
|
|
|
@ -70,12 +70,10 @@ class TransactionListItem extends ActionListItem with Keyable {
|
||||||
}
|
}
|
||||||
|
|
||||||
String get formattedStatus {
|
String get formattedStatus {
|
||||||
if (transaction.direction == TransactionDirection.incoming) {
|
if (balanceViewModel.wallet.type == WalletType.monero ||
|
||||||
if (balanceViewModel.wallet.type == WalletType.monero ||
|
balanceViewModel.wallet.type == WalletType.wownero ||
|
||||||
balanceViewModel.wallet.type == WalletType.wownero ||
|
balanceViewModel.wallet.type == WalletType.haven) {
|
||||||
balanceViewModel.wallet.type == WalletType.haven) {
|
return formattedPendingStatus;
|
||||||
return formattedPendingStatus;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return transaction.isPending ? S.current.pending : '';
|
return transaction.isPending ? S.current.pending : '';
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import 'package:cake_wallet/exchange/provider/exolix_exchange_provider.dart';
|
||||||
import 'package:cake_wallet/exchange/provider/quantex_exchange_provider.dart';
|
import 'package:cake_wallet/exchange/provider/quantex_exchange_provider.dart';
|
||||||
import 'package:cake_wallet/exchange/provider/sideshift_exchange_provider.dart';
|
import 'package:cake_wallet/exchange/provider/sideshift_exchange_provider.dart';
|
||||||
import 'package:cake_wallet/exchange/provider/simpleswap_exchange_provider.dart';
|
import 'package:cake_wallet/exchange/provider/simpleswap_exchange_provider.dart';
|
||||||
|
import 'package:cake_wallet/exchange/provider/stealth_ex_exchange_provider.dart';
|
||||||
import 'package:cake_wallet/exchange/provider/thorchain_exchange.provider.dart';
|
import 'package:cake_wallet/exchange/provider/thorchain_exchange.provider.dart';
|
||||||
import 'package:cake_wallet/exchange/provider/trocador_exchange_provider.dart';
|
import 'package:cake_wallet/exchange/provider/trocador_exchange_provider.dart';
|
||||||
import 'package:cake_wallet/exchange/trade.dart';
|
import 'package:cake_wallet/exchange/trade.dart';
|
||||||
|
@ -52,6 +53,8 @@ abstract class ExchangeTradeViewModelBase with Store {
|
||||||
case ExchangeProviderDescription.quantex:
|
case ExchangeProviderDescription.quantex:
|
||||||
_provider = QuantexExchangeProvider();
|
_provider = QuantexExchangeProvider();
|
||||||
break;
|
break;
|
||||||
|
case ExchangeProviderDescription.stealthEx:
|
||||||
|
_provider = StealthExExchangeProvider();
|
||||||
case ExchangeProviderDescription.thorChain:
|
case ExchangeProviderDescription.thorChain:
|
||||||
_provider = ThorChainExchangeProvider(tradesStore: trades);
|
_provider = ThorChainExchangeProvider(tradesStore: trades);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -4,6 +4,7 @@ import 'dart:convert';
|
||||||
|
|
||||||
import 'package:bitcoin_base/bitcoin_base.dart';
|
import 'package:bitcoin_base/bitcoin_base.dart';
|
||||||
import 'package:cake_wallet/core/create_trade_result.dart';
|
import 'package:cake_wallet/core/create_trade_result.dart';
|
||||||
|
import 'package:cake_wallet/exchange/provider/stealth_ex_exchange_provider.dart';
|
||||||
import 'package:cw_core/crypto_currency.dart';
|
import 'package:cw_core/crypto_currency.dart';
|
||||||
import 'package:cw_core/sync_status.dart';
|
import 'package:cw_core/sync_status.dart';
|
||||||
import 'package:cw_core/transaction_priority.dart';
|
import 'package:cw_core/transaction_priority.dart';
|
||||||
|
@ -160,15 +161,16 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
|
||||||
final SharedPreferences sharedPreferences;
|
final SharedPreferences sharedPreferences;
|
||||||
|
|
||||||
List<ExchangeProvider> get _allProviders => [
|
List<ExchangeProvider> get _allProviders => [
|
||||||
ChangeNowExchangeProvider(settingsStore: _settingsStore),
|
ChangeNowExchangeProvider(settingsStore: _settingsStore),
|
||||||
SideShiftExchangeProvider(),
|
SideShiftExchangeProvider(),
|
||||||
SimpleSwapExchangeProvider(),
|
SimpleSwapExchangeProvider(),
|
||||||
ThorChainExchangeProvider(tradesStore: trades),
|
ThorChainExchangeProvider(tradesStore: trades),
|
||||||
if (FeatureFlag.isExolixEnabled) ExolixExchangeProvider(),
|
if (FeatureFlag.isExolixEnabled) ExolixExchangeProvider(),
|
||||||
QuantexExchangeProvider(),
|
QuantexExchangeProvider(),
|
||||||
TrocadorExchangeProvider(
|
StealthExExchangeProvider(),
|
||||||
useTorOnly: _useTorOnly, providerStates: _settingsStore.trocadorProviderStates),
|
TrocadorExchangeProvider(
|
||||||
];
|
useTorOnly: _useTorOnly, providerStates: _settingsStore.trocadorProviderStates),
|
||||||
|
];
|
||||||
|
|
||||||
@observable
|
@observable
|
||||||
ExchangeProvider? provider;
|
ExchangeProvider? provider;
|
||||||
|
|
|
@ -62,7 +62,7 @@ abstract class WalletRestorationFromQRVMBase extends WalletCreationVM with Store
|
||||||
derivationInfo = options["derivationInfo"] as DerivationInfo?;
|
derivationInfo = options["derivationInfo"] as DerivationInfo?;
|
||||||
passphrase = options["passphrase"] as String?;
|
passphrase = options["passphrase"] as String?;
|
||||||
}
|
}
|
||||||
derivationInfo ??= getDefaultDerivation();
|
derivationInfo ??= getDefaultCreateDerivation();
|
||||||
|
|
||||||
switch (restoreWallet.restoreMode) {
|
switch (restoreWallet.restoreMode) {
|
||||||
case WalletRestoreMode.keys:
|
case WalletRestoreMode.keys:
|
||||||
|
|
|
@ -46,7 +46,7 @@ abstract class RestoreFromBackupViewModelBase with Store {
|
||||||
final data = await file.readAsBytes();
|
final data = await file.readAsBytes();
|
||||||
|
|
||||||
await backupService.importBackup(data, password);
|
await backupService.importBackup(data, password);
|
||||||
await main();
|
await initializeAppAtRoot(reInitializing: true);
|
||||||
|
|
||||||
final store = getIt.get<AppStore>();
|
final store = getIt.get<AppStore>();
|
||||||
ReactionDisposer? reaction;
|
ReactionDisposer? reaction;
|
||||||
|
|
|
@ -7,6 +7,7 @@ import 'package:cake_wallet/exchange/provider/exolix_exchange_provider.dart';
|
||||||
import 'package:cake_wallet/exchange/provider/quantex_exchange_provider.dart';
|
import 'package:cake_wallet/exchange/provider/quantex_exchange_provider.dart';
|
||||||
import 'package:cake_wallet/exchange/provider/sideshift_exchange_provider.dart';
|
import 'package:cake_wallet/exchange/provider/sideshift_exchange_provider.dart';
|
||||||
import 'package:cake_wallet/exchange/provider/simpleswap_exchange_provider.dart';
|
import 'package:cake_wallet/exchange/provider/simpleswap_exchange_provider.dart';
|
||||||
|
import 'package:cake_wallet/exchange/provider/stealth_ex_exchange_provider.dart';
|
||||||
import 'package:cake_wallet/exchange/provider/thorchain_exchange.provider.dart';
|
import 'package:cake_wallet/exchange/provider/thorchain_exchange.provider.dart';
|
||||||
import 'package:cake_wallet/exchange/provider/trocador_exchange_provider.dart';
|
import 'package:cake_wallet/exchange/provider/trocador_exchange_provider.dart';
|
||||||
import 'package:cake_wallet/exchange/trade.dart';
|
import 'package:cake_wallet/exchange/trade.dart';
|
||||||
|
@ -60,6 +61,9 @@ abstract class TradeDetailsViewModelBase with Store {
|
||||||
case ExchangeProviderDescription.quantex:
|
case ExchangeProviderDescription.quantex:
|
||||||
_provider = QuantexExchangeProvider();
|
_provider = QuantexExchangeProvider();
|
||||||
break;
|
break;
|
||||||
|
case ExchangeProviderDescription.stealthEx:
|
||||||
|
_provider = StealthExExchangeProvider();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
_updateItems();
|
_updateItems();
|
||||||
|
@ -86,6 +90,8 @@ abstract class TradeDetailsViewModelBase with Store {
|
||||||
return 'https://track.ninerealms.com/${trade.id}';
|
return 'https://track.ninerealms.com/${trade.id}';
|
||||||
case ExchangeProviderDescription.quantex:
|
case ExchangeProviderDescription.quantex:
|
||||||
return 'https://myquantex.com/send/${trade.id}';
|
return 'https://myquantex.com/send/${trade.id}';
|
||||||
|
case ExchangeProviderDescription.stealthEx:
|
||||||
|
return 'https://stealthex.io/exchange/?id=${trade.id}';
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -97,7 +97,7 @@ abstract class WalletCreationVMBase with Store {
|
||||||
dirPath: dirPath,
|
dirPath: dirPath,
|
||||||
address: '',
|
address: '',
|
||||||
showIntroCakePayCard: (!walletCreationService.typeExists(type)) && type != WalletType.haven,
|
showIntroCakePayCard: (!walletCreationService.typeExists(type)) && type != WalletType.haven,
|
||||||
derivationInfo: credentials.derivationInfo ?? getDefaultDerivation(),
|
derivationInfo: credentials.derivationInfo ?? getDefaultCreateDerivation(),
|
||||||
hardwareWalletType: credentials.hardwareWalletType,
|
hardwareWalletType: credentials.hardwareWalletType,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -116,7 +116,7 @@ abstract class WalletCreationVMBase with Store {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DerivationInfo? getDefaultDerivation() {
|
DerivationInfo? getDefaultCreateDerivation() {
|
||||||
final useBip39 = seedSettingsViewModel.bitcoinSeedType.type == DerivationType.bip39;
|
final useBip39 = seedSettingsViewModel.bitcoinSeedType.type == DerivationType.bip39;
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case WalletType.nano:
|
case WalletType.nano:
|
||||||
|
@ -147,10 +147,14 @@ abstract class WalletCreationVMBase with Store {
|
||||||
}
|
}
|
||||||
|
|
||||||
DerivationInfo? getCommonRestoreDerivation() {
|
DerivationInfo? getCommonRestoreDerivation() {
|
||||||
|
final useElectrum = seedSettingsViewModel.bitcoinSeedType.type == DerivationType.electrum;
|
||||||
switch (this.type) {
|
switch (this.type) {
|
||||||
case WalletType.nano:
|
case WalletType.nano:
|
||||||
return DerivationInfo(derivationType: DerivationType.nano);
|
return DerivationInfo(derivationType: DerivationType.nano);
|
||||||
case WalletType.bitcoin:
|
case WalletType.bitcoin:
|
||||||
|
if (useElectrum) {
|
||||||
|
return bitcoin!.getElectrumDerivations()[DerivationType.electrum]!.first;
|
||||||
|
}
|
||||||
return DerivationInfo(
|
return DerivationInfo(
|
||||||
derivationType: DerivationType.bip39,
|
derivationType: DerivationType.bip39,
|
||||||
derivationPath: "m/84'/0'/0'/0",
|
derivationPath: "m/84'/0'/0'/0",
|
||||||
|
@ -158,6 +162,9 @@ abstract class WalletCreationVMBase with Store {
|
||||||
scriptType: "p2wpkh",
|
scriptType: "p2wpkh",
|
||||||
);
|
);
|
||||||
case WalletType.litecoin:
|
case WalletType.litecoin:
|
||||||
|
if (useElectrum) {
|
||||||
|
return bitcoin!.getElectrumDerivations()[DerivationType.electrum]!.first;
|
||||||
|
}
|
||||||
return DerivationInfo(
|
return DerivationInfo(
|
||||||
derivationType: DerivationType.bip39,
|
derivationType: DerivationType.bip39,
|
||||||
derivationPath: "m/84'/2'/0'/0",
|
derivationPath: "m/84'/2'/0'/0",
|
||||||
|
|
|
@ -42,7 +42,8 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
|
||||||
type == WalletType.tron,
|
type == WalletType.tron,
|
||||||
isButtonEnabled = false,
|
isButtonEnabled = false,
|
||||||
mode = WalletRestoreMode.seed,
|
mode = WalletRestoreMode.seed,
|
||||||
super(appStore, walletInfoSource, walletCreationService, seedSettingsViewModel, type: type, isRecovery: true) {
|
super(appStore, walletInfoSource, walletCreationService, seedSettingsViewModel,
|
||||||
|
type: type, isRecovery: true) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case WalletType.monero:
|
case WalletType.monero:
|
||||||
availableModes = WalletRestoreMode.values;
|
availableModes = WalletRestoreMode.values;
|
||||||
|
@ -194,10 +195,11 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
|
||||||
|
|
||||||
case WalletType.nano:
|
case WalletType.nano:
|
||||||
return nano!.createNanoRestoreWalletFromKeysCredentials(
|
return nano!.createNanoRestoreWalletFromKeysCredentials(
|
||||||
name: name,
|
name: name,
|
||||||
password: password,
|
password: password,
|
||||||
seedKey: options['private_key'] as String,
|
seedKey: options['private_key'] as String,
|
||||||
derivationType: options["derivationType"] as DerivationType);
|
derivationType: derivationInfo!.derivationType!,
|
||||||
|
);
|
||||||
case WalletType.polygon:
|
case WalletType.polygon:
|
||||||
return polygon!.createPolygonRestoreWalletFromPrivateKey(
|
return polygon!.createPolygonRestoreWalletFromPrivateKey(
|
||||||
name: name,
|
name: name,
|
||||||
|
|
|
@ -8,7 +8,7 @@ if [[ ! -d "monero_c" ]];
|
||||||
then
|
then
|
||||||
git clone https://github.com/mrcyjanek/monero_c --branch rewrite-wip
|
git clone https://github.com/mrcyjanek/monero_c --branch rewrite-wip
|
||||||
cd monero_c
|
cd monero_c
|
||||||
git checkout 5de323b1ba7387cf73973042f06383d4dbe619f5
|
git checkout 3cb38bee9385faf46b03fd73aab85f3ac4115bf7
|
||||||
git reset --hard
|
git reset --hard
|
||||||
git submodule update --init --force --recursive
|
git submodule update --init --force --recursive
|
||||||
./apply_patches.sh monero
|
./apply_patches.sh monero
|
||||||
|
|
|
@ -43,6 +43,8 @@ class SecretKey {
|
||||||
SecretKey('cakePayApiKey', () => ''),
|
SecretKey('cakePayApiKey', () => ''),
|
||||||
SecretKey('CSRFToken', () => ''),
|
SecretKey('CSRFToken', () => ''),
|
||||||
SecretKey('authorization', () => ''),
|
SecretKey('authorization', () => ''),
|
||||||
|
SecretKey('stealthExBearerToken', () => ''),
|
||||||
|
SecretKey('stealthExAdditionalFeePercent', () => ''),
|
||||||
];
|
];
|
||||||
|
|
||||||
static final evmChainsSecrets = [
|
static final evmChainsSecrets = [
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue