mirror of
https://github.com/cake-tech/cake_wallet.git
synced 2025-03-27 09:38:50 +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
.github/workflows
assets
cw_monero
cw_nano/lib
cw_wownero
lib
scripts
tool/utils
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 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 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
|
||||
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 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 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
|
||||
run: |
|
||||
|
|
BIN
assets/images/stealthex.png
Normal file
BIN
assets/images/stealthex.png
Normal file
Binary file not shown.
After ![]() (image error) Size: 7.4 KiB |
|
@ -17,6 +17,3 @@
|
|||
-
|
||||
uri: node.community.rino.io:18081
|
||||
is_default: false
|
||||
-
|
||||
uri: node.moneroworld.com:18089
|
||||
is_default: false
|
||||
|
|
|
@ -138,11 +138,17 @@ PendingTransactionDescription createTransactionMultDestSync(
|
|||
int accountIndex = 0,
|
||||
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(
|
||||
wptr!,
|
||||
dstAddr: outputs.map((e) => e.address).toList(),
|
||||
dstAddr: dstAddrs,
|
||||
isSweepAll: false,
|
||||
amounts: outputs.map((e) => monero.Wallet_amountFromString(e.amount)).toList(),
|
||||
amounts: amounts,
|
||||
mixinCount: 0,
|
||||
pendingTransactionPriority: priorityRaw,
|
||||
subaddr_account: accountIndex,
|
||||
|
@ -307,7 +313,34 @@ class Transaction {
|
|||
confirmations = monero.TransactionInfo_confirmations(txInfo),
|
||||
fee = monero.TransactionInfo_fee(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({
|
||||
required this.displayLabel,
|
||||
|
|
|
@ -463,8 +463,8 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
path: "impls/monero.dart"
|
||||
ref: bcb328a4956105dc182afd0ce2e48fe263f5f20b
|
||||
resolved-ref: bcb328a4956105dc182afd0ce2e48fe263f5f20b
|
||||
ref: 3cb38bee9385faf46b03fd73aab85f3ac4115bf7
|
||||
resolved-ref: 3cb38bee9385faf46b03fd73aab85f3ac4115bf7
|
||||
url: "https://github.com/mrcyjanek/monero_c"
|
||||
source: git
|
||||
version: "0.0.0"
|
||||
|
|
|
@ -25,7 +25,7 @@ dependencies:
|
|||
monero:
|
||||
git:
|
||||
url: https://github.com/mrcyjanek/monero_c
|
||||
ref: bcb328a4956105dc182afd0ce2e48fe263f5f20b # monero_c hash
|
||||
ref: 3cb38bee9385faf46b03fd73aab85f3ac4115bf7 # monero_c hash
|
||||
path: impls/monero.dart
|
||||
mutex: ^3.1.0
|
||||
|
||||
|
|
|
@ -466,21 +466,25 @@ class NanoClient {
|
|||
|
||||
blocks = blocks as Map<String, dynamic>;
|
||||
|
||||
// confirm all receivable blocks:
|
||||
for (final blockHash in blocks.keys) {
|
||||
final block = blocks[blockHash];
|
||||
final String amountRaw = block["amount"] as String;
|
||||
await receiveBlock(
|
||||
blockHash: blockHash,
|
||||
amountRaw: amountRaw,
|
||||
privateKey: privateKey,
|
||||
destinationAddress: destinationAddress,
|
||||
);
|
||||
// a bit of a hack:
|
||||
await Future<void>.delayed(const Duration(seconds: 2));
|
||||
try {
|
||||
// confirm all receivable blocks:
|
||||
for (final blockHash in blocks.keys) {
|
||||
final block = blocks[blockHash];
|
||||
final String amountRaw = block["amount"] as String;
|
||||
await receiveBlock(
|
||||
blockHash: blockHash,
|
||||
amountRaw: amountRaw,
|
||||
privateKey: privateKey,
|
||||
destinationAddress: destinationAddress,
|
||||
);
|
||||
// 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() {}
|
||||
|
|
|
@ -14,8 +14,11 @@ import 'package:bip39/bip39.dart' as bip39;
|
|||
import 'package:nanodart/nanodart.dart';
|
||||
import 'package:nanoutil/nanoutil.dart';
|
||||
|
||||
class NanoWalletService extends WalletService<NanoNewWalletCredentials,
|
||||
NanoRestoreWalletFromSeedCredentials, NanoRestoreWalletFromKeysCredentials, NanoNewWalletCredentials> {
|
||||
class NanoWalletService extends WalletService<
|
||||
NanoNewWalletCredentials,
|
||||
NanoRestoreWalletFromSeedCredentials,
|
||||
NanoRestoreWalletFromKeysCredentials,
|
||||
NanoNewWalletCredentials> {
|
||||
NanoWalletService(this.walletInfoSource, this.isDirect);
|
||||
|
||||
final Box<WalletInfo> walletInfoSource;
|
||||
|
@ -33,8 +36,12 @@ class NanoWalletService extends WalletService<NanoNewWalletCredentials,
|
|||
String seedKey = NanoSeeds.generateSeed();
|
||||
String mnemonic = NanoDerivations.standardSeedToMnemonic(seedKey);
|
||||
|
||||
// ensure default if not present:
|
||||
credentials.walletInfo!.derivationInfo ??= DerivationInfo(derivationType: DerivationType.nano);
|
||||
// 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 = NanoWallet(
|
||||
walletInfo: credentials.walletInfo!,
|
||||
|
@ -86,7 +93,8 @@ class NanoWalletService extends WalletService<NanoNewWalletCredentials,
|
|||
}
|
||||
|
||||
@override
|
||||
Future<NanoWallet> restoreFromKeys(NanoRestoreWalletFromKeysCredentials credentials, {bool? isTestnet}) async {
|
||||
Future<NanoWallet> restoreFromKeys(NanoRestoreWalletFromKeysCredentials credentials,
|
||||
{bool? isTestnet}) async {
|
||||
if (credentials.seedKey.contains(' ')) {
|
||||
throw Exception("Invalid key!");
|
||||
} 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(
|
||||
password: credentials.password!,
|
||||
mnemonic: mnemonic ?? credentials.seedKey,
|
||||
|
@ -119,11 +134,13 @@ class NanoWalletService extends WalletService<NanoNewWalletCredentials,
|
|||
|
||||
@override
|
||||
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
|
||||
Future<NanoWallet> restoreFromSeed(NanoRestoreWalletFromSeedCredentials credentials, {bool? isTestnet}) async {
|
||||
Future<NanoWallet> restoreFromSeed(NanoRestoreWalletFromSeedCredentials credentials,
|
||||
{bool? isTestnet}) async {
|
||||
if (credentials.mnemonic.contains(' ')) {
|
||||
if (!bip39.validateMnemonic(credentials.mnemonic)) {
|
||||
throw nm.NanoMnemonicIsIncorrectException();
|
||||
|
|
|
@ -463,8 +463,8 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
path: "impls/monero.dart"
|
||||
ref: bcb328a4956105dc182afd0ce2e48fe263f5f20b
|
||||
resolved-ref: bcb328a4956105dc182afd0ce2e48fe263f5f20b
|
||||
ref: 3cb38bee9385faf46b03fd73aab85f3ac4115bf7
|
||||
resolved-ref: 3cb38bee9385faf46b03fd73aab85f3ac4115bf7
|
||||
url: "https://github.com/mrcyjanek/monero_c"
|
||||
source: git
|
||||
version: "0.0.0"
|
||||
|
|
|
@ -25,7 +25,7 @@ dependencies:
|
|||
monero:
|
||||
git:
|
||||
url: https://github.com/mrcyjanek/monero_c
|
||||
ref: bcb328a4956105dc182afd0ce2e48fe263f5f20b # monero_c hash
|
||||
ref: 3cb38bee9385faf46b03fd73aab85f3ac4115bf7 # monero_c hash
|
||||
path: impls/monero.dart
|
||||
mutex: ^3.1.0
|
||||
|
||||
|
|
|
@ -40,6 +40,7 @@ const solanaDefaultNodeUri = 'rpc.ankr.com';
|
|||
const tronDefaultNodeUri = 'trx.nownodes.io';
|
||||
const newCakeWalletBitcoinUri = 'btc-electrum.cakewallet.com:50002';
|
||||
const wowneroDefaultNodeUri = 'node3.monerodevs.org:34568';
|
||||
const moneroWorldNodeUri = '.moneroworld.com';
|
||||
|
||||
Future<void> defaultSettingsMigration(
|
||||
{required int version,
|
||||
|
@ -245,6 +246,9 @@ Future<void> defaultSettingsMigration(
|
|||
_fixNodesUseSSLFlag(nodes);
|
||||
await changeDefaultNanoNode(nodes, sharedPreferences);
|
||||
break;
|
||||
case 40:
|
||||
await removeMoneroWorld(sharedPreferences: sharedPreferences, nodes: nodes);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -488,15 +492,7 @@ Node? getBitcoinCashDefaultElectrumServer({required Box<Node> nodes}) {
|
|||
|
||||
Node getMoneroDefaultNode({required Box<Node> nodes}) {
|
||||
final timeZone = DateTime.now().timeZoneOffset.inHours;
|
||||
var nodeUri = '';
|
||||
|
||||
if (timeZone >= 1) {
|
||||
// Eurasia
|
||||
nodeUri = 'xmr-node-eu.cakewallet.com:18081';
|
||||
} else if (timeZone <= -4) {
|
||||
// America
|
||||
nodeUri = 'xmr-node-usa-east.cakewallet.com:18081';
|
||||
}
|
||||
var nodeUri = newCakeWalletMoneroUri;
|
||||
|
||||
try {
|
||||
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
|
||||
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');
|
||||
static const quantex =
|
||||
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}) {
|
||||
switch (raw) {
|
||||
|
@ -50,6 +52,8 @@ class ExchangeProviderDescription extends EnumerableItem<int> with Serializable<
|
|||
return thorChain;
|
||||
case 9:
|
||||
return quantex;
|
||||
case 10:
|
||||
return stealthEx;
|
||||
default:
|
||||
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 sending = TradeState(raw: 'sending', title: 'Sending');
|
||||
static const success = TradeState(raw: 'success', title: 'Success');
|
||||
|
||||
static TradeState deserialize({required String raw}) {
|
||||
|
||||
switch (raw) {
|
||||
|
@ -119,6 +118,7 @@ class TradeState extends EnumerableItem<String> with Serializable<String> {
|
|||
case 'refunded':
|
||||
return refunded;
|
||||
case 'confirmation':
|
||||
case 'verifying':
|
||||
return confirmation;
|
||||
case 'confirmed':
|
||||
return confirmed;
|
||||
|
|
|
@ -49,11 +49,14 @@ final rootKey = GlobalKey<RootState>();
|
|||
final RouteObserver<PageRoute<dynamic>> routeObserver = RouteObserver<PageRoute<dynamic>>();
|
||||
|
||||
Future<void> main() async {
|
||||
await runAppWithZone();
|
||||
}
|
||||
|
||||
Future<void> runAppWithZone() async {
|
||||
bool isAppRunning = false;
|
||||
|
||||
await runZonedGuarded(() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
FlutterError.onError = ExceptionHandler.onError;
|
||||
|
||||
/// A callback that is invoked when an unhandled error occurs in the root
|
||||
|
@ -63,42 +66,14 @@ Future<void> main() async {
|
|||
|
||||
return true;
|
||||
};
|
||||
|
||||
await setDefaultMinimumWindowSize();
|
||||
|
||||
await CakeHive.close();
|
||||
|
||||
await initializeAppConfigs();
|
||||
await initializeAppAtRoot();
|
||||
|
||||
runApp(App());
|
||||
|
||||
isAppRunning = true;
|
||||
}, (error, stackTrace) async {
|
||||
if (!isAppRunning) {
|
||||
runApp(
|
||||
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),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
TopLevelErrorWidget(error: error, stackTrace: stackTrace),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
setRootDirFromEnv();
|
||||
final appDir = await getAppDir();
|
||||
|
@ -210,7 +191,7 @@ Future<void> initializeAppConfigs() async {
|
|||
transactionDescriptions: transactionDescriptions,
|
||||
secureStorage: secureStorage,
|
||||
anonpayInvoiceInfo: anonpayInvoiceInfo,
|
||||
initialMigrationVersion: 39,
|
||||
initialMigrationVersion: 40,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -342,3 +323,41 @@ class _HomeState extends State<_Home> {
|
|||
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,
|
||||
orElse: () => N2Node(
|
||||
account: currentRepAccount,
|
||||
alias: currentRepAccount,
|
||||
score: 0,
|
||||
uptime: "???",
|
||||
weight: 0,
|
||||
),
|
||||
);
|
||||
|
||||
return currentNode;
|
||||
}
|
||||
|
||||
|
@ -57,9 +55,7 @@ class NanoChangeRepPage extends BasePage {
|
|||
child: FutureBuilder(
|
||||
future: nano!.getN2Reps(_wallet),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.data == null) {
|
||||
return SizedBox();
|
||||
}
|
||||
final reps = snapshot.data ?? [];
|
||||
|
||||
return Container(
|
||||
padding: EdgeInsets.only(left: 24, right: 24),
|
||||
|
@ -101,29 +97,35 @@ class NanoChangeRepPage extends BasePage {
|
|||
),
|
||||
_buildSingleRepresentative(
|
||||
context,
|
||||
getCurrentRepNode(snapshot.data as List<N2Node>),
|
||||
getCurrentRepNode(reps),
|
||||
isList: false,
|
||||
divider: false,
|
||||
),
|
||||
Divider(height: 20),
|
||||
Container(
|
||||
margin: EdgeInsets.only(top: 12),
|
||||
child: Text(
|
||||
S.current.nano_pick_new_rep,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w700,
|
||||
if (reps.isNotEmpty) ...[
|
||||
Divider(height: 20),
|
||||
Container(
|
||||
margin: EdgeInsets.only(top: 12),
|
||||
child: Text(
|
||||
S.current.nano_pick_new_rep,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Divider(height: 20),
|
||||
],
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
contentPadding: EdgeInsets.only(bottom: 24),
|
||||
content: Container(
|
||||
child: Column(
|
||||
children: _getRepresentativeWidgets(context, snapshot.data as List<N2Node>),
|
||||
),
|
||||
child: reps.isNotEmpty
|
||||
? Column(
|
||||
children: _getRepresentativeWidgets(context, reps),
|
||||
)
|
||||
: SizedBox(),
|
||||
),
|
||||
bottomSectionPadding: EdgeInsets.only(bottom: 24),
|
||||
bottomSection: Observer(
|
||||
|
@ -207,19 +209,22 @@ class NanoChangeRepPage extends BasePage {
|
|||
final List<Widget> ret = [];
|
||||
for (final N2Node node in list) {
|
||||
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;
|
||||
}
|
||||
|
||||
Widget _buildSingleRepresentative(BuildContext context, N2Node rep, {bool isList = true}) {
|
||||
Widget _buildSingleRepresentative(
|
||||
BuildContext context,
|
||||
N2Node rep, {
|
||||
bool isList = true,
|
||||
bool divider = false,
|
||||
}) {
|
||||
return Column(
|
||||
children: <Widget>[
|
||||
if (isList)
|
||||
Divider(
|
||||
height: 2,
|
||||
),
|
||||
if (divider) Divider(height: 2),
|
||||
TextButton(
|
||||
style: TextButton.styleFrom(
|
||||
padding: EdgeInsets.zero,
|
||||
|
@ -244,11 +249,11 @@ class NanoChangeRepPage extends BasePage {
|
|||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
_sanitizeAlias(rep.alias),
|
||||
rep.alias ?? rep.account!,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
|
||||
fontWeight: FontWeight.w700,
|
||||
fontSize: 18,
|
||||
fontSize: rep.alias == null ? 14 : 18,
|
||||
),
|
||||
),
|
||||
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?;
|
||||
} else if (derivationsWithHistory == 1) {
|
||||
dInfo = derivations[derivationWithHistoryIndex];
|
||||
}
|
||||
|
||||
// get the default derivation for this wallet type:
|
||||
if (dInfo == null) {
|
||||
} else if (derivations.length == 1) {
|
||||
// we only return 1 derivation if we're pretty sure we know which one to use:
|
||||
if (derivations.length == 1) {
|
||||
dInfo = derivations.first;
|
||||
} else {
|
||||
// if we have multiple possible derivations, and none have histories
|
||||
// we just default to the most common one:
|
||||
dInfo = walletRestoreViewModel.getCommonRestoreDerivation();
|
||||
}
|
||||
dInfo = derivations.first;
|
||||
} else {
|
||||
// if we have multiple possible derivations, and none (or multiple) have histories
|
||||
// we just default to the most common one:
|
||||
dInfo = walletRestoreViewModel.getCommonRestoreDerivation();
|
||||
}
|
||||
|
||||
this.derivationInfo = dInfo;
|
||||
if (this.derivationInfo == null) {
|
||||
this.derivationInfo = walletRestoreViewModel.getDefaultDerivation();
|
||||
}
|
||||
|
||||
await walletRestoreViewModel.create(options: _credentials());
|
||||
seedSettingsViewModel.setPassphrase(null);
|
||||
|
|
|
@ -273,6 +273,7 @@ class SendPage extends BasePage {
|
|||
? template.cryptoCurrency
|
||||
: template.fiatCurrency,
|
||||
onTap: () async {
|
||||
sendViewModel.state = IsExecutingState();
|
||||
if (template.additionalRecipients?.isNotEmpty ?? false) {
|
||||
sendViewModel.clearOutputs();
|
||||
|
||||
|
@ -301,6 +302,7 @@ class SendPage extends BasePage {
|
|||
template: template,
|
||||
);
|
||||
}
|
||||
sendViewModel.state = InitialExecutionState();
|
||||
},
|
||||
onRemove: () {
|
||||
showPopUp<void>(
|
||||
|
@ -368,6 +370,7 @@ class SendPage extends BasePage {
|
|||
builder: (_) {
|
||||
return LoadingPrimaryButton(
|
||||
onPressed: () async {
|
||||
if (sendViewModel.state is IsExecutingState) return;
|
||||
if (_formKey.currentState != null && !_formKey.currentState!.validate()) {
|
||||
if (sendViewModel.outputs.length > 1) {
|
||||
showErrorValidationAlert(context);
|
||||
|
|
|
@ -113,10 +113,6 @@ class CheckBoxPickerState extends State<CheckBoxPicker> {
|
|||
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
if (item.isDisabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool newValue = !item.value;
|
||||
item.value = newValue;
|
||||
widget.onChanged(index, newValue);
|
||||
|
@ -134,7 +130,7 @@ class CheckBoxPickerState extends State<CheckBoxPicker> {
|
|||
borderColor: Theme.of(context).dividerColor,
|
||||
iconColor: Colors.white,
|
||||
onChanged: (bool? value) {
|
||||
if (value == null || item.isDisabled) {
|
||||
if (value == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -16,7 +16,8 @@ abstract class TradeFilterStoreBase with Store {
|
|||
displaySimpleSwap = true,
|
||||
displayTrocador = true,
|
||||
displayExolix = true,
|
||||
displayThorChain = true;
|
||||
displayThorChain = true,
|
||||
displayStealthEx = true;
|
||||
|
||||
@observable
|
||||
bool displayXMRTO;
|
||||
|
@ -42,6 +43,9 @@ abstract class TradeFilterStoreBase with Store {
|
|||
@observable
|
||||
bool displayThorChain;
|
||||
|
||||
@observable
|
||||
bool displayStealthEx;
|
||||
|
||||
@computed
|
||||
bool get displayAllTrades =>
|
||||
displayChangeNow &&
|
||||
|
@ -49,7 +53,8 @@ abstract class TradeFilterStoreBase with Store {
|
|||
displaySimpleSwap &&
|
||||
displayTrocador &&
|
||||
displayExolix &&
|
||||
displayThorChain;
|
||||
displayThorChain &&
|
||||
displayStealthEx;
|
||||
|
||||
@action
|
||||
void toggleDisplayExchange(ExchangeProviderDescription provider) {
|
||||
|
@ -78,6 +83,9 @@ abstract class TradeFilterStoreBase with Store {
|
|||
case ExchangeProviderDescription.thorChain:
|
||||
displayThorChain = !displayThorChain;
|
||||
break;
|
||||
case ExchangeProviderDescription.stealthEx:
|
||||
displayStealthEx = !displayStealthEx;
|
||||
break;
|
||||
case ExchangeProviderDescription.all:
|
||||
if (displayAllTrades) {
|
||||
displayChangeNow = false;
|
||||
|
@ -88,6 +96,7 @@ abstract class TradeFilterStoreBase with Store {
|
|||
displayTrocador = false;
|
||||
displayExolix = false;
|
||||
displayThorChain = false;
|
||||
displayStealthEx = false;
|
||||
} else {
|
||||
displayChangeNow = true;
|
||||
displaySideShift = true;
|
||||
|
@ -97,6 +106,7 @@ abstract class TradeFilterStoreBase with Store {
|
|||
displayTrocador = true;
|
||||
displayExolix = true;
|
||||
displayThorChain = true;
|
||||
displayStealthEx = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -112,13 +122,19 @@ abstract class TradeFilterStoreBase with Store {
|
|||
? _trades
|
||||
.where((item) =>
|
||||
(displayXMRTO && item.trade.provider == ExchangeProviderDescription.xmrto) ||
|
||||
(displaySideShift && item.trade.provider == ExchangeProviderDescription.sideShift) ||
|
||||
(displayChangeNow && item.trade.provider == ExchangeProviderDescription.changeNow) ||
|
||||
(displayMorphToken && item.trade.provider == ExchangeProviderDescription.morphToken) ||
|
||||
(displaySimpleSwap && item.trade.provider == ExchangeProviderDescription.simpleSwap) ||
|
||||
(displaySideShift &&
|
||||
item.trade.provider == ExchangeProviderDescription.sideShift) ||
|
||||
(displayChangeNow &&
|
||||
item.trade.provider == ExchangeProviderDescription.changeNow) ||
|
||||
(displayMorphToken &&
|
||||
item.trade.provider == ExchangeProviderDescription.morphToken) ||
|
||||
(displaySimpleSwap &&
|
||||
item.trade.provider == ExchangeProviderDescription.simpleSwap) ||
|
||||
(displayTrocador && item.trade.provider == ExchangeProviderDescription.trocador) ||
|
||||
(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()
|
||||
: _trades;
|
||||
}
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
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/core/key_service.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/exchange_api_mode.dart';
|
||||
import 'package:cake_wallet/entities/preferences_key.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/exchange/exchange_provider_description.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:eth_sig_util/util/utils.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:mobx/mobx.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:cake_wallet/.secrets.g.dart' as secrets;
|
||||
|
||||
part 'dashboard_view_model.g.dart';
|
||||
|
||||
|
@ -129,6 +129,11 @@ abstract class DashboardViewModelBase with Store {
|
|||
caption: ExchangeProviderDescription.thorChain.title,
|
||||
onChanged: () =>
|
||||
tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.thorChain)),
|
||||
FilterItem(
|
||||
value: () => tradeFilterStore.displayStealthEx,
|
||||
caption: ExchangeProviderDescription.stealthEx.title,
|
||||
onChanged: () =>
|
||||
tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.stealthEx)),
|
||||
]
|
||||
},
|
||||
subname = '',
|
||||
|
|
|
@ -70,12 +70,10 @@ class TransactionListItem extends ActionListItem with Keyable {
|
|||
}
|
||||
|
||||
String get formattedStatus {
|
||||
if (transaction.direction == TransactionDirection.incoming) {
|
||||
if (balanceViewModel.wallet.type == WalletType.monero ||
|
||||
balanceViewModel.wallet.type == WalletType.wownero ||
|
||||
balanceViewModel.wallet.type == WalletType.haven) {
|
||||
return formattedPendingStatus;
|
||||
}
|
||||
if (balanceViewModel.wallet.type == WalletType.monero ||
|
||||
balanceViewModel.wallet.type == WalletType.wownero ||
|
||||
balanceViewModel.wallet.type == WalletType.haven) {
|
||||
return formattedPendingStatus;
|
||||
}
|
||||
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/sideshift_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/trocador_exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/trade.dart';
|
||||
|
@ -52,6 +53,8 @@ abstract class ExchangeTradeViewModelBase with Store {
|
|||
case ExchangeProviderDescription.quantex:
|
||||
_provider = QuantexExchangeProvider();
|
||||
break;
|
||||
case ExchangeProviderDescription.stealthEx:
|
||||
_provider = StealthExExchangeProvider();
|
||||
case ExchangeProviderDescription.thorChain:
|
||||
_provider = ThorChainExchangeProvider(tradesStore: trades);
|
||||
break;
|
||||
|
|
|
@ -4,6 +4,7 @@ import 'dart:convert';
|
|||
|
||||
import 'package:bitcoin_base/bitcoin_base.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/sync_status.dart';
|
||||
import 'package:cw_core/transaction_priority.dart';
|
||||
|
@ -160,15 +161,16 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
|
|||
final SharedPreferences sharedPreferences;
|
||||
|
||||
List<ExchangeProvider> get _allProviders => [
|
||||
ChangeNowExchangeProvider(settingsStore: _settingsStore),
|
||||
SideShiftExchangeProvider(),
|
||||
SimpleSwapExchangeProvider(),
|
||||
ThorChainExchangeProvider(tradesStore: trades),
|
||||
if (FeatureFlag.isExolixEnabled) ExolixExchangeProvider(),
|
||||
QuantexExchangeProvider(),
|
||||
TrocadorExchangeProvider(
|
||||
useTorOnly: _useTorOnly, providerStates: _settingsStore.trocadorProviderStates),
|
||||
];
|
||||
ChangeNowExchangeProvider(settingsStore: _settingsStore),
|
||||
SideShiftExchangeProvider(),
|
||||
SimpleSwapExchangeProvider(),
|
||||
ThorChainExchangeProvider(tradesStore: trades),
|
||||
if (FeatureFlag.isExolixEnabled) ExolixExchangeProvider(),
|
||||
QuantexExchangeProvider(),
|
||||
StealthExExchangeProvider(),
|
||||
TrocadorExchangeProvider(
|
||||
useTorOnly: _useTorOnly, providerStates: _settingsStore.trocadorProviderStates),
|
||||
];
|
||||
|
||||
@observable
|
||||
ExchangeProvider? provider;
|
||||
|
|
|
@ -62,7 +62,7 @@ abstract class WalletRestorationFromQRVMBase extends WalletCreationVM with Store
|
|||
derivationInfo = options["derivationInfo"] as DerivationInfo?;
|
||||
passphrase = options["passphrase"] as String?;
|
||||
}
|
||||
derivationInfo ??= getDefaultDerivation();
|
||||
derivationInfo ??= getDefaultCreateDerivation();
|
||||
|
||||
switch (restoreWallet.restoreMode) {
|
||||
case WalletRestoreMode.keys:
|
||||
|
|
|
@ -46,7 +46,7 @@ abstract class RestoreFromBackupViewModelBase with Store {
|
|||
final data = await file.readAsBytes();
|
||||
|
||||
await backupService.importBackup(data, password);
|
||||
await main();
|
||||
await initializeAppAtRoot(reInitializing: true);
|
||||
|
||||
final store = getIt.get<AppStore>();
|
||||
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/sideshift_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/trocador_exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/trade.dart';
|
||||
|
@ -60,6 +61,9 @@ abstract class TradeDetailsViewModelBase with Store {
|
|||
case ExchangeProviderDescription.quantex:
|
||||
_provider = QuantexExchangeProvider();
|
||||
break;
|
||||
case ExchangeProviderDescription.stealthEx:
|
||||
_provider = StealthExExchangeProvider();
|
||||
break;
|
||||
}
|
||||
|
||||
_updateItems();
|
||||
|
@ -86,6 +90,8 @@ abstract class TradeDetailsViewModelBase with Store {
|
|||
return 'https://track.ninerealms.com/${trade.id}';
|
||||
case ExchangeProviderDescription.quantex:
|
||||
return 'https://myquantex.com/send/${trade.id}';
|
||||
case ExchangeProviderDescription.stealthEx:
|
||||
return 'https://stealthex.io/exchange/?id=${trade.id}';
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -97,7 +97,7 @@ abstract class WalletCreationVMBase with Store {
|
|||
dirPath: dirPath,
|
||||
address: '',
|
||||
showIntroCakePayCard: (!walletCreationService.typeExists(type)) && type != WalletType.haven,
|
||||
derivationInfo: credentials.derivationInfo ?? getDefaultDerivation(),
|
||||
derivationInfo: credentials.derivationInfo ?? getDefaultCreateDerivation(),
|
||||
hardwareWalletType: credentials.hardwareWalletType,
|
||||
);
|
||||
|
||||
|
@ -116,7 +116,7 @@ abstract class WalletCreationVMBase with Store {
|
|||
}
|
||||
}
|
||||
|
||||
DerivationInfo? getDefaultDerivation() {
|
||||
DerivationInfo? getDefaultCreateDerivation() {
|
||||
final useBip39 = seedSettingsViewModel.bitcoinSeedType.type == DerivationType.bip39;
|
||||
switch (type) {
|
||||
case WalletType.nano:
|
||||
|
@ -147,10 +147,14 @@ abstract class WalletCreationVMBase with Store {
|
|||
}
|
||||
|
||||
DerivationInfo? getCommonRestoreDerivation() {
|
||||
final useElectrum = seedSettingsViewModel.bitcoinSeedType.type == DerivationType.electrum;
|
||||
switch (this.type) {
|
||||
case WalletType.nano:
|
||||
return DerivationInfo(derivationType: DerivationType.nano);
|
||||
case WalletType.bitcoin:
|
||||
if (useElectrum) {
|
||||
return bitcoin!.getElectrumDerivations()[DerivationType.electrum]!.first;
|
||||
}
|
||||
return DerivationInfo(
|
||||
derivationType: DerivationType.bip39,
|
||||
derivationPath: "m/84'/0'/0'/0",
|
||||
|
@ -158,6 +162,9 @@ abstract class WalletCreationVMBase with Store {
|
|||
scriptType: "p2wpkh",
|
||||
);
|
||||
case WalletType.litecoin:
|
||||
if (useElectrum) {
|
||||
return bitcoin!.getElectrumDerivations()[DerivationType.electrum]!.first;
|
||||
}
|
||||
return DerivationInfo(
|
||||
derivationType: DerivationType.bip39,
|
||||
derivationPath: "m/84'/2'/0'/0",
|
||||
|
|
|
@ -42,7 +42,8 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
|
|||
type == WalletType.tron,
|
||||
isButtonEnabled = false,
|
||||
mode = WalletRestoreMode.seed,
|
||||
super(appStore, walletInfoSource, walletCreationService, seedSettingsViewModel, type: type, isRecovery: true) {
|
||||
super(appStore, walletInfoSource, walletCreationService, seedSettingsViewModel,
|
||||
type: type, isRecovery: true) {
|
||||
switch (type) {
|
||||
case WalletType.monero:
|
||||
availableModes = WalletRestoreMode.values;
|
||||
|
@ -194,10 +195,11 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
|
|||
|
||||
case WalletType.nano:
|
||||
return nano!.createNanoRestoreWalletFromKeysCredentials(
|
||||
name: name,
|
||||
password: password,
|
||||
seedKey: options['private_key'] as String,
|
||||
derivationType: options["derivationType"] as DerivationType);
|
||||
name: name,
|
||||
password: password,
|
||||
seedKey: options['private_key'] as String,
|
||||
derivationType: derivationInfo!.derivationType!,
|
||||
);
|
||||
case WalletType.polygon:
|
||||
return polygon!.createPolygonRestoreWalletFromPrivateKey(
|
||||
name: name,
|
||||
|
|
|
@ -8,7 +8,7 @@ if [[ ! -d "monero_c" ]];
|
|||
then
|
||||
git clone https://github.com/mrcyjanek/monero_c --branch rewrite-wip
|
||||
cd monero_c
|
||||
git checkout 5de323b1ba7387cf73973042f06383d4dbe619f5
|
||||
git checkout 3cb38bee9385faf46b03fd73aab85f3ac4115bf7
|
||||
git reset --hard
|
||||
git submodule update --init --force --recursive
|
||||
./apply_patches.sh monero
|
||||
|
|
|
@ -43,6 +43,8 @@ class SecretKey {
|
|||
SecretKey('cakePayApiKey', () => ''),
|
||||
SecretKey('CSRFToken', () => ''),
|
||||
SecretKey('authorization', () => ''),
|
||||
SecretKey('stealthExBearerToken', () => ''),
|
||||
SecretKey('stealthExAdditionalFeePercent', () => ''),
|
||||
];
|
||||
|
||||
static final evmChainsSecrets = [
|
||||
|
|
Loading…
Reference in a new issue