Merge branch 'main' of https://github.com/cake-tech/cake_wallet into CW-659-Automated-Integrated-Tests

This commit is contained in:
Blazebrain 2024-09-11 06:10:32 +01:00
commit 63e08480b4
184 changed files with 4033 additions and 790 deletions

View file

@ -23,9 +23,10 @@ jobs:
docker-images: true
- uses: actions/checkout@v2
- uses: actions/setup-java@v1
- uses: actions/setup-java@v2
with:
java-version: "17.x"
distribution: "temurin"
java-version: "17"
- name: Configure placeholder git details
run: |
git config --global user.email "CI@cakewallet.com"

View file

@ -42,9 +42,10 @@ jobs:
docker-images: true
- uses: actions/checkout@v2
- uses: actions/setup-java@v1
- uses: actions/setup-java@v2
with:
java-version: "17.x"
distribution: "temurin"
java-version: "17"
- name: Configure placeholder git details
run: |
git config --global user.email "CI@cakewallet.com"
@ -170,6 +171,10 @@ 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 letsExchangeBearerToken = '${{ secrets.LETS_EXCHANGE_TOKEN }}';" >> lib/.secrets.g.dart
echo "const letsExchangeAffiliateId = '${{ secrets.LETS_EXCHANGE_AFFILIATE_ID }}';" >> 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: |

View file

@ -154,6 +154,10 @@ 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 letsExchangeBearerToken = '${{ secrets.LETS_EXCHANGE_TOKEN }}';" >> lib/.secrets.g.dart
echo "const letsExchangeAffiliateId = '${{ secrets.LETS_EXCHANGE_AFFILIATE_ID }}';" >> 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: |

View file

@ -161,7 +161,9 @@ The only parts to be translated, if needed, are the values m and s after the var
4. Add the language to `lib/entities/language_service.dart` under both `supportedLocales` and `localeCountryCode`. Use the name of the language in the local language and in English in parentheses after for `supportedLocales`. Use the [ISO 3166-1 alpha-3 code](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-3) for `localeCountryCode`. You must choose one country, so choose the country with the most native speakers of this language or is otherwise best associated with this language.
5. Add a relevant flag to `assets/images/flags/XXXX.png`, replacing XXXX with the 3 digit localeCountryCode. The image must be 42x26 pixels with a 3 pixels of transparent margin on all 4 sides. You can resize the flag with [paint.net](https://www.getpaint.net/) to 36x20 pixels, expand the canvas to 42x26 pixels with the flag anchored in the middle, and then manually delete the 3 pixels on each side to make transparent. Or you can use another program like Photoshop.
5. Add a relevant flag to `assets/images/flags/XXXX.png`, replacing XXXX with the 3 letters localeCountryCode. The image must be 42x26 pixels with a 3 pixels of transparent margin on all 4 sides. You can resize the flag with [paint.net](https://www.getpaint.net/) to 36x20 pixels, expand the canvas to 42x26 pixels with the flag anchored in the middle, and then manually delete the 3 pixels on each side to make transparent. Or you can use another program like Photoshop.
6. Add the new language code to `tool/utils/translation/translation_constants.dart`
## Add a new fiat currency

View file

@ -10,7 +10,18 @@ analyzer:
lib/generated/*.dart,
cw_monero/ios/External/**,
cw_shared_external/**,
shared_external/**]
shared_external/**,
lib/bitcoin/cw_bitcoin.dart,
lib/bitcoin_cash/cw_bitcoin_cash.dart,
lib/ethereum/cw_ethereum.dart,
lib/haven/cw_haven.dart,
lib/monero/cw_monero.dart,
lib/nano/cw_nano.dart,
lib/polygon/cw_polygon.dart,
lib/solana/cw_solana.dart,
lib/tron/cw_tron.dart,
lib/wownero/cw_wownero.dart,
]
language:
strict-casts: true
strict-raw-types: true

Binary file not shown.

Before

Width:  |  Height:  |  Size: 287 B

After

Width:  |  Height:  |  Size: 376 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 340 B

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
assets/images/flags/arm.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 446 B

After

Width:  |  Height:  |  Size: 913 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 735 B

After

Width:  |  Height:  |  Size: 372 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 692 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1,005 B

After

Width:  |  Height:  |  Size: 788 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 855 B

After

Width:  |  Height:  |  Size: 371 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 351 B

After

Width:  |  Height:  |  Size: 753 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 860 B

After

Width:  |  Height:  |  Size: 840 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 217 B

After

Width:  |  Height:  |  Size: 381 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 915 B

After

Width:  |  Height:  |  Size: 830 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 703 B

After

Width:  |  Height:  |  Size: 370 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 801 B

After

Width:  |  Height:  |  Size: 387 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 431 B

After

Width:  |  Height:  |  Size: 553 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1,005 B

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 700 B

After

Width:  |  Height:  |  Size: 351 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 432 B

After

Width:  |  Height:  |  Size: 762 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 477 B

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1,023 B

After

Width:  |  Height:  |  Size: 2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 728 B

After

Width:  |  Height:  |  Size: 373 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 684 B

After

Width:  |  Height:  |  Size: 360 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 615 B

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 867 B

After

Width:  |  Height:  |  Size: 424 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 937 B

After

Width:  |  Height:  |  Size: 994 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 705 B

After

Width:  |  Height:  |  Size: 351 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 896 B

After

Width:  |  Height:  |  Size: 851 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 429 B

After

Width:  |  Height:  |  Size: 849 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1,013 B

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 902 B

After

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 193 B

After

Width:  |  Height:  |  Size: 351 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 762 B

After

Width:  |  Height:  |  Size: 372 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 898 B

After

Width:  |  Height:  |  Size: 424 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 899 B

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 707 B

After

Width:  |  Height:  |  Size: 366 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 705 B

After

Width:  |  Height:  |  Size: 351 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 702 B

After

Width:  |  Height:  |  Size: 371 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 750 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 476 B

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 902 B

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 752 B

After

Width:  |  Height:  |  Size: 390 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 774 B

After

Width:  |  Height:  |  Size: 384 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 373 B

After

Width:  |  Height:  |  Size: 1,005 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 597 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1,009 B

After

Width:  |  Height:  |  Size: 887 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 448 B

After

Width:  |  Height:  |  Size: 882 B

View file

@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" width="32" height="32">
<path fill-rule="evenodd" clip-rule="evenodd"
d="M16 1.37854C16 0.764286 16.6636 0.379192 17.1969 0.68395L23 4L29.4961 7.71208C29.8077 7.89012 30 8.22147 30 8.58032V16L23.9923 12.567C23.3774 12.2157 22.6226 12.2157 22.0077 12.567L16 16V8V1.37854ZM2 16V8.58032C2 8.22147 2.19229 7.89012 2.50386 7.71208L8.00772 4.56702C8.62259 4.21566 9.37741 4.21566 9.99228 4.56702L16 8L2 16ZM16 30.6215C16 31.2357 15.3364 31.6208 14.8031 31.3161L9 28L2.50386 24.2879C2.19229 24.1099 2 23.7785 2 23.4197V16L8.00772 19.433C8.62259 19.7843 9.37741 19.7843 9.99228 19.433L16 16V24V30.6215ZM22.0077 27.433C22.6226 27.7843 23.3774 27.7843 23.9923 27.433L29.4961 24.2879C29.8077 24.1099 30 23.7785 30 23.4197V16L16 24L22.0077 27.433Z"
fill="#159DFF"></path>
</svg>

After

Width:  |  Height:  |  Size: 846 B

BIN
assets/images/stealthex.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

View file

@ -17,6 +17,3 @@
-
uri: node.community.rino.io:18081
is_default: false
-
uri: node.moneroworld.com:18089
is_default: false

View file

@ -1,3 +1,2 @@
Scan and verify messages
Synchronization enhancements
Bug fixes
Enhance auto-address generation for Monero
Bug fixes and enhancements

View file

@ -1,3 +1,4 @@
Scan and verify messages
Synchronization enhancements
Bug fixes
Enable BIP39 by default for wallet creation also on Bitcoin/Litecoin (Electrum seed type is still accessible through advanced settings page)
Improve fee calculation for Bitcoin to protect against overpaying or underpaying
Enhance auto-address generation for Monero
Bug fixes and enhancements

View file

@ -1,6 +1,7 @@
import 'dart:convert';
import 'dart:math';
import 'dart:typed_data';
import 'package:crypto/crypto.dart';
import 'package:cryptography/cryptography.dart' as cryptography;
import 'package:cw_core/sec_random_native.dart';
@ -59,11 +60,7 @@ void maskBytes(Uint8List bytes, int bits) {
}
}
String bufferToBin(Uint8List data) {
final q1 = data.map((e) => e.toRadixString(2).padLeft(8, '0'));
final q2 = q1.join('');
return q2;
}
String bufferToBin(Uint8List data) => data.map((e) => e.toRadixString(2).padLeft(8, '0')).join('');
String encode(Uint8List data) {
final dataBitLen = data.length * 8;
@ -112,17 +109,18 @@ Future<bool> checkIfMnemonicIsElectrum2(String mnemonic) async {
Future<String> getMnemonicHash(String mnemonic) async {
final hmacSha512 = Hmac(sha512, utf8.encode('Seed version'));
final digest = hmacSha512.convert(utf8.encode(normalizeText(mnemonic)));
final hx = digest.toString();
return hx;
return digest.toString();
}
Future<Uint8List> mnemonicToSeedBytes(String mnemonic, {String prefix = segwit}) async {
Future<Uint8List> mnemonicToSeedBytes(String mnemonic,
{String prefix = segwit, String passphrase = ''}) async {
final pbkdf2 =
cryptography.Pbkdf2(macAlgorithm: cryptography.Hmac.sha512(), iterations: 2048, bits: 512);
final text = normalizeText(mnemonic);
// pbkdf2.deriveKey(secretKey: secretKey, nonce: nonce)
final passphraseBytes = utf8.encode(normalizeText(passphrase));
final key = await pbkdf2.deriveKey(
secretKey: cryptography.SecretKey(text.codeUnits), nonce: 'electrum'.codeUnits);
secretKey: cryptography.SecretKey(text.codeUnits),
nonce: [...'electrum'.codeUnits, ...passphraseBytes]);
final bytes = await key.extractBytes();
return Uint8List.fromList(bytes);
}

View file

@ -7,5 +7,6 @@ class MnemonicBip39 {
static String generate({int strength = 128}) => bip39.generateMnemonic(strength: strength);
/// Create root seed from mnemonic
static Uint8List toSeed(String mnemonic) => bip39.mnemonicToSeed(mnemonic);
static Uint8List toSeed(String mnemonic, {String? passphrase}) =>
bip39.mnemonicToSeed(mnemonic, passphrase: passphrase ?? '');
}

View file

@ -115,7 +115,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
break;
case DerivationType.electrum:
default:
seedBytes = await mnemonicToSeedBytes(mnemonic);
seedBytes = await mnemonicToSeedBytes(mnemonic, passphrase: passphrase ?? "");
break;
}
return BitcoinWallet(
@ -195,7 +195,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
if (mnemonic != null) {
switch (walletInfo.derivationInfo!.derivationType) {
case DerivationType.electrum:
seedBytes = await mnemonicToSeedBytes(mnemonic);
seedBytes = await mnemonicToSeedBytes(mnemonic, passphrase: passphrase ?? "");
break;
case DerivationType.bip39:
default:

View file

@ -3,16 +3,18 @@ import 'package:cw_core/wallet_credentials.dart';
import 'package:cw_core/wallet_info.dart';
class BitcoinNewWalletCredentials extends WalletCredentials {
BitcoinNewWalletCredentials(
{required String name,
BitcoinNewWalletCredentials({
required String name,
WalletInfo? walletInfo,
String? password,
DerivationType? derivationType,
String? derivationPath})
: super(
String? derivationPath,
String? passphrase,
}) : super(
name: name,
walletInfo: walletInfo,
password: password,
passphrase: passphrase,
);
}

View file

@ -1,6 +1,7 @@
import 'dart:io';
import 'package:bitcoin_base/bitcoin_base.dart';
import 'package:cw_bitcoin/bitcoin_mnemonic.dart';
import 'package:cw_bitcoin/bitcoin_mnemonics_bip39.dart';
import 'package:cw_bitcoin/mnemonic_is_incorrect_exception.dart';
import 'package:cw_bitcoin/bitcoin_wallet_creation_credentials.dart';
import 'package:cw_core/encryption_file_utils.dart';
@ -35,8 +36,21 @@ class BitcoinWalletService extends WalletService<
final network = isTestnet == true ? BitcoinNetwork.testnet : BitcoinNetwork.mainnet;
credentials.walletInfo?.network = network.value;
final String mnemonic;
switch ( credentials.walletInfo?.derivationInfo?.derivationType) {
case DerivationType.bip39:
final strength = credentials.seedPhraseLength == 24 ? 256 : 128;
mnemonic = await MnemonicBip39.generate(strength: strength);
break;
case DerivationType.electrum:
default:
mnemonic = await generateElectrumMnemonic();
break;
}
final wallet = await BitcoinWalletBase.create(
mnemonic: await generateElectrumMnemonic(),
mnemonic: mnemonic,
password: credentials.password!,
passphrase: credentials.passphrase,
walletInfo: credentials.walletInfo!,

View file

@ -107,21 +107,20 @@ class ElectrumClient {
}
},
onError: (Object error) {
socket = null;
final errorMsg = error.toString();
print(errorMsg);
unterminatedString = '';
final currentHost = socket?.address.host;
final isErrorForCurrentHost = errorMsg.contains(" ${currentHost} ");
if (currentHost != null && isErrorForCurrentHost)
_setConnectionStatus(ConnectionStatus.failed);
},
onDone: () {
socket = null;
unterminatedString = '';
if (host == socket?.address.host) _setConnectionStatus(ConnectionStatus.disconnected);
try {
if (host == socket?.address.host) {
socket?.destroy();
_setConnectionStatus(ConnectionStatus.disconnected);
}
} catch(e) {
print(e.toString());
}
},
cancelOnError: true,
);
@ -434,7 +433,6 @@ class ElectrumClient {
{required String id, required String method, List<Object> params = const []}) {
try {
if (socket == null) {
_setConnectionStatus(ConnectionStatus.failed);
return null;
}
final subscription = BehaviorSubject<T>();
@ -451,7 +449,6 @@ class ElectrumClient {
Future<dynamic> call(
{required String method, List<Object> params = const [], Function(int)? idCallback}) async {
if (socket == null) {
_setConnectionStatus(ConnectionStatus.failed);
return null;
}
final completer = Completer<dynamic>();
@ -465,10 +462,9 @@ class ElectrumClient {
}
Future<dynamic> callWithTimeout(
{required String method, List<Object> params = const [], int timeout = 4000}) async {
{required String method, List<Object> params = const [], int timeout = 5000}) async {
try {
if (socket == null) {
_setConnectionStatus(ConnectionStatus.failed);
return null;
}
final completer = Completer<dynamic>();

View file

@ -109,5 +109,4 @@ Map<DerivationType, List<DerivationInfo>> electrum_derivations = {
],
};
String electrum_path = electrum_derivations[DerivationType.electrum]!.first.derivationPath!;

View file

@ -1,3 +1,5 @@
import 'dart:convert';
import 'package:bitcoin_base/bitcoin_base.dart';
import 'package:cw_bitcoin/address_from_output.dart';
import 'package:cw_bitcoin/bitcoin_address_record.dart';
@ -7,10 +9,12 @@ import 'package:cw_core/transaction_direction.dart';
import 'package:cw_core/transaction_info.dart';
import 'package:cw_core/format_amount.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:hex/hex.dart';
class ElectrumTransactionBundle {
ElectrumTransactionBundle(this.originalTransaction,
{required this.ins, required this.confirmations, this.time});
final BtcTransaction originalTransaction;
final List<BtcTransaction> ins;
final int? time;
@ -125,7 +129,24 @@ class ElectrumTransactionInfo extends TransactionInfo {
for (final out in bundle.originalTransaction.outputs) {
totalOutAmount += out.amount.toInt();
final addressExists = addresses.contains(addressFromOutputScript(out.scriptPubKey, network));
outputAddresses.add(addressFromOutputScript(out.scriptPubKey, network));
final address = addressFromOutputScript(out.scriptPubKey, network);
if (address.isNotEmpty) outputAddresses.add(address);
// Check if the script contains OP_RETURN
final script = out.scriptPubKey.script;
if (script.contains('OP_RETURN')) {
final index = script.indexOf('OP_RETURN');
if (index + 1 <= script.length) {
try {
final opReturnData = script[index + 1].toString();
final decodedString = utf8.decode(HEX.decode(opReturnData));
outputAddresses.add('OP_RETURN:$decodedString');
} catch (_) {
outputAddresses.add('OP_RETURN:');
}
}
}
if (addressExists) {
receivedAmounts.add(out.amount.toInt());
@ -235,6 +256,6 @@ class ElectrumTransactionInfo extends TransactionInfo {
}
String toString() {
return 'ElectrumTransactionInfo(id: $id, height: $height, amount: $amount, fee: $fee, direction: $direction, date: $date, isPending: $isPending, confirmations: $confirmations, to: $to, unspent: $unspents)';
return 'ElectrumTransactionInfo(id: $id, height: $height, amount: $amount, fee: $fee, direction: $direction, date: $date, isPending: $isPending, confirmations: $confirmations, to: $to, unspent: $unspents, inputAddresses: $inputAddresses, outputAddresses: $outputAddresses)';
}
}

View file

@ -5,6 +5,7 @@ import 'dart:isolate';
import 'dart:math';
import 'package:bitcoin_base/bitcoin_base.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:cw_core/encryption_file_utils.dart';
import 'package:blockchain_utils/blockchain_utils.dart';
import 'package:collection/collection.dart';
@ -42,8 +43,9 @@ import 'package:flutter/foundation.dart';
import 'package:hive/hive.dart';
import 'package:mobx/mobx.dart';
import 'package:rxdart/subjects.dart';
import 'package:http/http.dart' as http;
import 'package:sp_scanner/sp_scanner.dart';
import 'package:hex/hex.dart';
import 'package:http/http.dart' as http;
part 'electrum_wallet.g.dart';
@ -89,7 +91,7 @@ abstract class ElectrumWalletBase
}
: {}),
this.unspentCoinsInfo = unspentCoinsInfo,
this.isTestnet = network == BitcoinNetwork.testnet,
this.isTestnet = !network.isMainnet,
this._mnemonic = mnemonic,
super(walletInfo) {
this.electrumClient = electrumClient ?? ElectrumClient();
@ -101,6 +103,8 @@ abstract class ElectrumWalletBase
);
reaction((_) => syncStatus, _syncStatusReaction);
sharedPrefs.complete(SharedPreferences.getInstance());
}
static Bip32Slip10Secp256k1 getAccountHDWallet(CryptoCurrency? currency, BasedUtxoNetwork network,
@ -133,9 +137,12 @@ abstract class ElectrumWalletBase
final String? _mnemonic;
Bip32Slip10Secp256k1 get hd => accountHD.childKey(Bip32KeyIndex(0));
Bip32Slip10Secp256k1 get sideHd => accountHD.childKey(Bip32KeyIndex(1));
final EncryptionFileUtils encryptionFileUtils;
@override
final String? passphrase;
@override
@ -182,7 +189,7 @@ abstract class ElectrumWalletBase
BasedUtxoNetwork network;
@override
bool? isTestnet;
bool isTestnet;
bool get hasSilentPaymentsScanning => type == WalletType.bitcoin;
@ -193,6 +200,13 @@ abstract class ElectrumWalletBase
bool _isTryingToConnect = false;
Completer<SharedPreferences> sharedPrefs = Completer();
Future<bool> checkIfMempoolAPIIsEnabled() async {
bool isMempoolAPIEnabled = (await sharedPrefs.future).getBool("use_mempool_fee_api") ?? true;
return isMempoolAPIEnabled;
}
@action
Future<void> setSilentPaymentsScanning(bool active) async {
silentPaymentsScanningActive = active;
@ -262,7 +276,6 @@ abstract class ElectrumWalletBase
Future<Isolate>? _isolate;
void Function(FlutterErrorDetails)? _onError;
Timer? _reconnectTimer;
Timer? _autoSaveTimer;
Timer? _updateFeeRateTimer;
static const int _autoSaveInterval = 1;
@ -415,6 +428,10 @@ abstract class ElectrumWalletBase
@override
Future<void> startSync() async {
try {
if (syncStatus is SyncronizingSyncStatus) {
return;
}
syncStatus = SyncronizingSyncStatus();
if (hasSilentPaymentsScanning) {
@ -426,7 +443,7 @@ abstract class ElectrumWalletBase
await updateTransactions();
await updateAllUnspents();
await updateBalance();
updateFeeRates();
await updateFeeRates();
_updateFeeRateTimer ??=
Timer.periodic(const Duration(minutes: 1), (timer) async => await updateFeeRates());
@ -445,9 +462,25 @@ abstract class ElectrumWalletBase
@action
Future<void> updateFeeRates() async {
if (await checkIfMempoolAPIIsEnabled()) {
try {
final response =
await http.get(Uri.parse("http://mempool.cakewallet.com:8999/api/v1/fees/recommended"));
final result = json.decode(response.body) as Map<String, num>;
final slowFee = result['economyFee']?.toInt() ?? 0;
final mediumFee = result['hourFee']?.toInt() ?? 0;
final fastFee = result['fastestFee']?.toInt() ?? 0;
_feeRates = [slowFee, mediumFee, fastFee];
return;
} catch (_) {}
}
final feeRates = await electrumClient.feeRates(network: network);
if (feeRates != [0, 0, 0]) {
_feeRates = feeRates;
} else if (isTestnet) {
_feeRates = [1, 1, 1];
}
}
@ -1005,7 +1038,7 @@ abstract class ElectrumWalletBase
if (estimatedTx.inputPrivKeyInfos.isEmpty) {
error += "\nNo private keys generated.";
} else {
error += "\nAddress: ${utxo.ownerDetails.address.toAddress()}";
error += "\nAddress: ${utxo.ownerDetails.address.toAddress(network)}";
key = estimatedTx.inputPrivKeyInfos.firstWhereOrNull((element) {
final elemPubkey = element.privkey.getPublic().toHex();
@ -1056,6 +1089,8 @@ abstract class ElectrumWalletBase
});
}
unspentCoins.removeWhere((utxo) => estimatedTx.utxos.any((e) => e.utxo.txHash == utxo.hash));
await updateBalance();
});
} catch (e) {
@ -1362,26 +1397,15 @@ abstract class ElectrumWalletBase
}
}
Future<bool> canReplaceByFee(String hash) async {
final verboseTransaction = await electrumClient.getTransactionVerbose(hash: hash);
final String? transactionHex;
int confirmations = 0;
if (verboseTransaction.isEmpty) {
transactionHex = await electrumClient.getTransactionHex(hash: hash);
} else {
confirmations = verboseTransaction['confirmations'] as int? ?? 0;
transactionHex = verboseTransaction['hex'] as String?;
}
if (confirmations > 0) return false;
if (transactionHex == null || transactionHex.isEmpty) {
Future<bool> canReplaceByFee(ElectrumTransactionInfo tx) async {
try {
final bundle = await getTransactionExpanded(hash: tx.txHash);
_updateInputsAndOutputs(tx, bundle);
if (bundle.confirmations > 0) return false;
return bundle.originalTransaction.canReplaceByFee;
} catch (e) {
return false;
}
return BtcTransaction.fromRaw(transactionHex).canReplaceByFee;
}
Future<bool> isChangeSufficientForFee(String txId, int newFee) async {
@ -1422,6 +1446,7 @@ abstract class ElectrumWalletBase
List<ECPrivate> privateKeys = [];
var allInputsAmount = 0;
String? memo;
// Add inputs
for (var i = 0; i < bundle.originalTransaction.inputs.length; i++) {
@ -1457,53 +1482,82 @@ abstract class ElectrumWalletBase
);
}
int totalOutAmount = bundle.originalTransaction.outputs
.fold<int>(0, (previousValue, element) => previousValue + element.amount.toInt());
var currentFee = allInputsAmount - totalOutAmount;
int remainingFee = newFee - currentFee;
// Create a list of available outputs
final outputs = <BitcoinOutput>[];
for (final out in bundle.originalTransaction.outputs) {
// Check if the script contains OP_RETURN
final script = out.scriptPubKey.script;
if (script.contains('OP_RETURN') && memo == null) {
final index = script.indexOf('OP_RETURN');
if (index + 1 <= script.length) {
try {
final opReturnData = script[index + 1].toString();
memo = utf8.decode(HEX.decode(opReturnData));
continue;
} catch (_) {
throw Exception('Cannot decode OP_RETURN data');
}
}
}
// Add outputs and deduct the fees from it
for (int i = bundle.originalTransaction.outputs.length - 1; i >= 0; i--) {
final out = bundle.originalTransaction.outputs[i];
final address = addressFromOutputScript(out.scriptPubKey, network);
final btcAddress = addressTypeFromStr(address, network);
int newAmount;
if (out.amount.toInt() >= remainingFee) {
newAmount = out.amount.toInt() - remainingFee;
remainingFee = 0;
// if new amount of output is less than dust amount, then don't add this output as well
if (newAmount <= _dustAmount) {
continue;
}
} else {
remainingFee -= out.amount.toInt();
continue;
outputs.add(BitcoinOutput(address: btcAddress, value: BigInt.from(out.amount.toInt())));
}
outputs.add(BitcoinOutput(address: btcAddress, value: BigInt.from(newAmount)));
// Calculate the total amount and fees
int totalOutAmount =
outputs.fold<int>(0, (previousValue, output) => previousValue + output.value.toInt());
int currentFee = allInputsAmount - totalOutAmount;
int remainingFee = newFee - currentFee;
if (remainingFee <= 0) {
throw Exception("New fee must be higher than the current fee.");
}
// Deduct Remaining Fee from Main Outputs
if (remainingFee > 0) {
for (int i = outputs.length - 1; i >= 0; i--) {
int outputAmount = outputs[i].value.toInt();
if (outputAmount > _dustAmount) {
int deduction = (outputAmount - _dustAmount >= remainingFee)
? remainingFee
: outputAmount - _dustAmount;
outputs[i] = BitcoinOutput(
address: outputs[i].address, value: BigInt.from(outputAmount - deduction));
remainingFee -= deduction;
if (remainingFee <= 0) break;
}
}
}
// Final check if the remaining fee couldn't be deducted
if (remainingFee > 0) {
throw Exception("Not enough funds to cover the fee.");
}
// Identify all change outputs
final changeAddresses = walletAddresses.allAddresses.where((element) => element.isHidden);
final List<BitcoinOutput> changeOutputs = outputs
.where((output) => changeAddresses
.any((element) => element.address == output.address.toAddress(network)))
.toList();
// look for a change address in the outputs
final changeOutput = outputs.firstWhereOrNull((output) =>
changeAddresses.any((element) => element.address == output.address.toAddress(network)));
int totalChangeAmount =
changeOutputs.fold<int>(0, (sum, output) => sum + output.value.toInt());
// deduct the change amount from the output amount
if (changeOutput != null) {
totalOutAmount -= changeOutput.value.toInt();
}
// The final amount that the receiver will receive
int sendingAmount = allInputsAmount - newFee - totalChangeAmount;
final txb = BitcoinTransactionBuilder(
utxos: utxos,
outputs: outputs,
fee: BigInt.from(newFee),
network: network,
memo: memo,
outputOrdering: BitcoinOrdering.none,
enableRBF: true,
);
@ -1526,10 +1580,10 @@ abstract class ElectrumWalletBase
transaction,
type,
electrumClient: electrumClient,
amount: totalOutAmount,
amount: sendingAmount,
fee: newFee,
network: network,
hasChange: changeOutput != null,
hasChange: changeOutputs.isNotEmpty,
feeRate: newFee.toString(),
)..addListener((transaction) async {
transactionHistory.addOne(transaction);
@ -1944,8 +1998,9 @@ abstract class ElectrumWalletBase
Future<void> _setInitialHeight() async {
if (_chainTipUpdateSubject != null) return;
_currentChainTip = await getUpdatedChainTip();
if ((_currentChainTip == null || _currentChainTip! == 0) && walletInfo.restoreHeight == 0) {
await getUpdatedChainTip();
await walletInfo.updateRestoreHeight(_currentChainTip!);
}
@ -2005,9 +2060,8 @@ abstract class ElectrumWalletBase
_isTryingToConnect = true;
_reconnectTimer?.cancel();
_reconnectTimer = Timer(Duration(seconds: 10), () {
if (this.syncStatus is! SyncedSyncStatus && this.syncStatus is! SyncedTipSyncStatus) {
Timer(Duration(seconds: 5), () {
if (this.syncStatus is NotConnectedSyncStatus || this.syncStatus is LostConnectionSyncStatus) {
this.electrumClient.connectToUri(
node!.uri,
useSSL: node!.useSSL ?? false,
@ -2024,6 +2078,54 @@ abstract class ElectrumWalletBase
});
}
}
void _updateInputsAndOutputs(ElectrumTransactionInfo tx, ElectrumTransactionBundle bundle) {
tx.inputAddresses = tx.inputAddresses?.where((address) => address.isNotEmpty).toList();
if (tx.inputAddresses == null ||
tx.inputAddresses!.isEmpty ||
tx.outputAddresses == null ||
tx.outputAddresses!.isEmpty) {
List<String> inputAddresses = [];
List<String> outputAddresses = [];
for (int i = 0; i < bundle.originalTransaction.inputs.length; i++) {
final input = bundle.originalTransaction.inputs[i];
final inputTransaction = bundle.ins[i];
final vout = input.txIndex;
final outTransaction = inputTransaction.outputs[vout];
final address = addressFromOutputScript(outTransaction.scriptPubKey, network);
if (address.isNotEmpty) inputAddresses.add(address);
}
for (int i = 0; i < bundle.originalTransaction.outputs.length; i++) {
final out = bundle.originalTransaction.outputs[i];
final address = addressFromOutputScript(out.scriptPubKey, network);
if (address.isNotEmpty) outputAddresses.add(address);
// Check if the script contains OP_RETURN
final script = out.scriptPubKey.script;
if (script.contains('OP_RETURN')) {
final index = script.indexOf('OP_RETURN');
if (index + 1 <= script.length) {
try {
final opReturnData = script[index + 1].toString();
final decodedString = utf8.decode(HEX.decode(opReturnData));
outputAddresses.add('OP_RETURN:$decodedString');
} catch (_) {
outputAddresses.add('OP_RETURN:');
}
}
}
}
tx.inputAddresses = inputAddresses;
tx.outputAddresses = outputAddresses;
transactionHistory.addOne(tx);
}
}
}
class ScanNode {

View file

@ -7,6 +7,7 @@ import 'package:bip39/bip39.dart' as bip39;
import 'package:cw_bitcoin/bitcoin_address_record.dart';
import 'package:cw_bitcoin/bitcoin_mnemonic.dart';
import 'package:cw_bitcoin/bitcoin_transaction_priority.dart';
import 'package:cw_bitcoin/electrum_derivations.dart';
import 'package:cw_core/encryption_file_utils.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/unspent_coins_info.dart';
@ -36,6 +37,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
required Box<UnspentCoinsInfo> unspentCoinsInfo,
required Uint8List seedBytes,
required EncryptionFileUtils encryptionFileUtils,
String? passphrase,
String? addressPageType,
List<BitcoinAddressRecord>? initialAddresses,
ElectrumBalance? initialBalance,
@ -51,6 +53,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
initialBalance: initialBalance,
seedBytes: seedBytes,
encryptionFileUtils: encryptionFileUtils,
passphrase: passphrase,
currency: CryptoCurrency.ltc) {
walletAddresses = LitecoinWalletAddresses(
walletInfo,
@ -89,7 +92,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
break;
case DerivationType.electrum:
default:
seedBytes = await mnemonicToSeedBytes(mnemonic);
seedBytes = await mnemonicToSeedBytes(mnemonic, passphrase: passphrase ?? "");
break;
}
return LitecoinWallet(
@ -100,6 +103,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
initialAddresses: initialAddresses,
initialBalance: initialBalance,
encryptionFileUtils: encryptionFileUtils,
passphrase: passphrase,
seedBytes: seedBytes,
initialRegularAddressIndex: initialRegularAddressIndex,
initialChangeAddressIndex: initialChangeAddressIndex,
@ -143,6 +147,31 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
);
}
walletInfo.derivationInfo ??= DerivationInfo();
// set the default if not present:
walletInfo.derivationInfo!.derivationPath ??= snp?.derivationPath ?? electrum_path;
walletInfo.derivationInfo!.derivationType ??= snp?.derivationType ?? DerivationType.electrum;
Uint8List? seedBytes = null;
final mnemonic = keysData.mnemonic;
final passphrase = keysData.passphrase;
if (mnemonic != null) {
switch (walletInfo.derivationInfo?.derivationType) {
case DerivationType.bip39:
seedBytes = await bip39.mnemonicToSeed(
mnemonic,
passphrase: passphrase ?? "",
);
break;
case DerivationType.electrum:
default:
seedBytes = await mnemonicToSeedBytes(mnemonic, passphrase: passphrase ?? "");
break;
}
}
return LitecoinWallet(
mnemonic: keysData.mnemonic!,
password: password,
@ -150,7 +179,8 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
unspentCoinsInfo: unspentCoinsInfo,
initialAddresses: snp?.addresses,
initialBalance: snp?.balance,
seedBytes: await mnemonicToSeedBytes(keysData.mnemonic!),
seedBytes: seedBytes!,
passphrase: passphrase,
encryptionFileUtils: encryptionFileUtils,
initialRegularAddressIndex: snp?.regularAddressIndex,
initialChangeAddressIndex: snp?.changeAddressIndex,

View file

@ -1,4 +1,5 @@
import 'dart:io';
import 'package:cw_bitcoin/bitcoin_mnemonics_bip39.dart';
import 'package:cw_core/encryption_file_utils.dart';
import 'package:cw_core/unspent_coins_info.dart';
import 'package:hive/hive.dart';
@ -30,8 +31,21 @@ class LitecoinWalletService extends WalletService<
@override
Future<LitecoinWallet> create(BitcoinNewWalletCredentials credentials, {bool? isTestnet}) async {
final String mnemonic;
switch ( credentials.walletInfo?.derivationInfo?.derivationType) {
case DerivationType.bip39:
final strength = credentials.seedPhraseLength == 24 ? 256 : 128;
mnemonic = await MnemonicBip39.generate(strength: strength);
break;
case DerivationType.electrum:
default:
mnemonic = await generateElectrumMnemonic();
break;
}
final wallet = await LitecoinWalletBase.create(
mnemonic: await generateElectrumMnemonic(),
mnemonic: mnemonic,
password: credentials.password!,
passphrase: credentials.passphrase,
walletInfo: credentials.walletInfo!,

View file

@ -350,6 +350,11 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
flutter_web_plugins:
dependency: transitive
description: flutter
source: sdk
version: "0.0.0"
frontend_server_client:
dependency: transitive
description:
@ -736,6 +741,62 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.27.7"
shared_preferences:
dependency: "direct main"
description:
name: shared_preferences
sha256: d3bbe5553a986e83980916ded2f0b435ef2e1893dfaa29d5a7a790d0eca12180
url: "https://pub.dev"
source: hosted
version: "2.2.3"
shared_preferences_android:
dependency: transitive
description:
name: shared_preferences_android
sha256: "1ee8bf911094a1b592de7ab29add6f826a7331fb854273d55918693d5364a1f2"
url: "https://pub.dev"
source: hosted
version: "2.2.2"
shared_preferences_foundation:
dependency: transitive
description:
name: shared_preferences_foundation
sha256: c4b35f6cb8f63c147312c054ce7c2254c8066745125264f0c88739c417fc9d9f
url: "https://pub.dev"
source: hosted
version: "2.5.2"
shared_preferences_linux:
dependency: transitive
description:
name: shared_preferences_linux
sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f"
url: "https://pub.dev"
source: hosted
version: "2.4.1"
shared_preferences_platform_interface:
dependency: transitive
description:
name: shared_preferences_platform_interface
sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80"
url: "https://pub.dev"
source: hosted
version: "2.4.1"
shared_preferences_web:
dependency: transitive
description:
name: shared_preferences_web
sha256: "59dc807b94d29d52ddbb1b3c0d3b9d0a67fc535a64e62a5542c8db0513fcb6c2"
url: "https://pub.dev"
source: hosted
version: "2.4.1"
shared_preferences_windows:
dependency: transitive
description:
name: shared_preferences_windows
sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1"
url: "https://pub.dev"
source: hosted
version: "2.4.1"
shelf:
dependency: transitive
description:
@ -944,4 +1005,4 @@ packages:
version: "2.2.1"
sdks:
dart: ">=3.3.0 <4.0.0"
flutter: ">=3.16.6"
flutter: ">=3.19.0"

View file

@ -17,6 +17,7 @@ dependencies:
mobx: ^2.0.7+4
flutter_mobx: ^2.0.6+1
intl: ^0.18.0
shared_preferences: ^2.0.15
cw_core:
path: ../cw_core
bitbox:

View file

@ -3,5 +3,4 @@ export 'bitcoin_cash_wallet_addresses.dart';
export 'bitcoin_cash_wallet_creation_credentials.dart';
export 'bitcoin_cash_wallet_service.dart';
export 'exceptions/exceptions.dart';
export 'mnemonic.dart';
export 'bitcoin_cash_address_utils.dart';

View file

@ -1,13 +1,14 @@
import 'package:bitbox/bitbox.dart' as bitbox;
import 'package:bitcoin_base/bitcoin_base.dart';
import 'package:blockchain_utils/blockchain_utils.dart';
import 'package:cw_core/encryption_file_utils.dart';
import 'package:cw_bitcoin/bitcoin_address_record.dart';
import 'package:cw_bitcoin/bitcoin_mnemonics_bip39.dart';
import 'package:cw_bitcoin/bitcoin_transaction_priority.dart';
import 'package:cw_bitcoin/electrum_balance.dart';
import 'package:cw_bitcoin/electrum_wallet.dart';
import 'package:cw_bitcoin/electrum_wallet_snapshot.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/encryption_file_utils.dart';
import 'package:cw_core/transaction_priority.dart';
import 'package:cw_core/unspent_coins_info.dart';
import 'package:cw_core/wallet_info.dart';
@ -30,6 +31,7 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store {
required Box<UnspentCoinsInfo> unspentCoinsInfo,
required Uint8List seedBytes,
required EncryptionFileUtils encryptionFileUtils,
String? passphrase,
BitcoinAddressType? addressPageType,
List<BitcoinAddressRecord>? initialAddresses,
ElectrumBalance? initialBalance,
@ -45,7 +47,8 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store {
initialBalance: initialBalance,
seedBytes: seedBytes,
currency: CryptoCurrency.bch,
encryptionFileUtils: encryptionFileUtils) {
encryptionFileUtils: encryptionFileUtils,
passphrase: passphrase) {
walletAddresses = BitcoinCashWalletAddresses(
walletInfo,
initialAddresses: initialAddresses,
@ -67,6 +70,7 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store {
required WalletInfo walletInfo,
required Box<UnspentCoinsInfo> unspentCoinsInfo,
required EncryptionFileUtils encryptionFileUtils,
String? passphrase,
String? addressPageType,
List<BitcoinAddressRecord>? initialAddresses,
ElectrumBalance? initialBalance,
@ -79,11 +83,12 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store {
unspentCoinsInfo: unspentCoinsInfo,
initialAddresses: initialAddresses,
initialBalance: initialBalance,
seedBytes: await MnemonicBip39.toSeed(mnemonic),
seedBytes: await MnemonicBip39.toSeed(mnemonic, passphrase: passphrase),
encryptionFileUtils: encryptionFileUtils,
initialRegularAddressIndex: initialRegularAddressIndex,
initialChangeAddressIndex: initialChangeAddressIndex,
addressPageType: P2pkhAddressType.p2pkh,
passphrase: passphrase,
);
}
@ -150,11 +155,12 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store {
}
}).toList(),
initialBalance: snp?.balance,
seedBytes: await MnemonicBip39.toSeed(keysData.mnemonic!),
seedBytes: await MnemonicBip39.toSeed(keysData.mnemonic!, passphrase: keysData.passphrase),
encryptionFileUtils: encryptionFileUtils,
initialRegularAddressIndex: snp?.regularAddressIndex,
initialChangeAddressIndex: snp?.changeAddressIndex,
addressPageType: P2pkhAddressType.p2pkh,
passphrase: keysData.passphrase,
);
}

View file

@ -2,17 +2,19 @@ import 'package:cw_core/wallet_credentials.dart';
import 'package:cw_core/wallet_info.dart';
class BitcoinCashNewWalletCredentials extends WalletCredentials {
BitcoinCashNewWalletCredentials({required String name, WalletInfo? walletInfo, String? password})
: super(name: name, walletInfo: walletInfo, password: password);
BitcoinCashNewWalletCredentials(
{required String name, WalletInfo? walletInfo, String? password, String? passphrase})
: super(name: name, walletInfo: walletInfo, password: password, passphrase: passphrase);
}
class BitcoinCashRestoreWalletFromSeedCredentials extends WalletCredentials {
BitcoinCashRestoreWalletFromSeedCredentials(
{required String name,
BitcoinCashRestoreWalletFromSeedCredentials({
required String name,
required String password,
required this.mnemonic,
WalletInfo? walletInfo})
: super(name: name, password: password, walletInfo: walletInfo);
WalletInfo? walletInfo,
String? passphrase,
}) : super(name: name, password: password, walletInfo: walletInfo, passphrase: passphrase);
final String mnemonic;
}

View file

@ -1,6 +1,8 @@
import 'dart:io';
import 'package:bip39/bip39.dart';
import 'package:collection/collection.dart';
import 'package:cw_bitcoin/bitcoin_mnemonics_bip39.dart';
import 'package:cw_bitcoin_cash/cw_bitcoin_cash.dart';
import 'package:cw_core/encryption_file_utils.dart';
import 'package:cw_core/pathForWallet.dart';
@ -9,7 +11,6 @@ import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:cw_core/wallet_service.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:collection/collection.dart';
import 'package:hive/hive.dart';
class BitcoinCashWalletService extends WalletService<
@ -40,6 +41,7 @@ class BitcoinCashWalletService extends WalletService<
walletInfo: credentials.walletInfo!,
unspentCoinsInfo: unspentCoinsInfoSource,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
passphrase: credentials.passphrase,
);
await wallet.save();
await wallet.init();
@ -130,7 +132,9 @@ class BitcoinCashWalletService extends WalletService<
mnemonic: credentials.mnemonic,
walletInfo: credentials.walletInfo!,
unspentCoinsInfo: unspentCoinsInfoSource,
encryptionFileUtils: encryptionFileUtilsFor(isDirect));
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
passphrase: credentials.passphrase
);
await wallet.save();
await wallet.init();
return wallet;

View file

@ -46,6 +46,8 @@ abstract class WalletBase<BalanceType extends Balance, HistoryType extends Trans
String? get hexSeed => null;
String? get passphrase => null;
Object get keys;
WalletAddresses get walletAddresses;
@ -95,5 +97,5 @@ abstract class WalletBase<BalanceType extends Balance, HistoryType extends Trans
Future<bool> verifyMessage(String message, String signature, {String? address = null});
bool? isTestnet;
bool isTestnet = false;
}

View file

@ -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,

View file

@ -13,11 +13,9 @@ import 'package:cw_core/monero_transaction_priority.dart';
import 'package:cw_core/monero_wallet_keys.dart';
import 'package:cw_core/monero_wallet_utils.dart';
import 'package:cw_core/node.dart';
import 'package:cw_core/pathForWallet.dart';
import 'package:cw_core/pending_transaction.dart';
import 'package:cw_core/sync_status.dart';
import 'package:cw_core/transaction_direction.dart';
import 'package:cw_core/transaction_priority.dart';
import 'package:cw_core/unspent_coins_info.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/wallet_info.dart';

View file

@ -9,11 +9,9 @@ import 'package:cw_core/wallet_info.dart';
import 'package:cw_core/wallet_service.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:cw_core/get_height_by_date.dart';
import 'package:cw_monero/api/exceptions/wallet_opening_exception.dart';
import 'package:cw_monero/api/wallet_manager.dart' as monero_wallet_manager;
import 'package:cw_monero/api/wallet_manager.dart';
import 'package:cw_monero/monero_wallet.dart';
import 'package:flutter/widgets.dart';
import 'package:hive/hive.dart';
import 'package:polyseed/polyseed.dart';
import 'package:monero/monero.dart' as monero;
@ -120,7 +118,6 @@ class MoneroWalletService extends WalletService<
@override
Future<MoneroWallet> openWallet(String name, String password, {bool? retryOnFailure}) async {
MoneroWallet? wallet;
try {
final path = await pathForWallet(name: name, type: getType());
@ -147,41 +144,10 @@ class MoneroWalletService extends WalletService<
await wallet.init();
return wallet;
} catch (e, s) {
} catch (e) {
// TODO: Implement Exception for wallet list service.
final bool isBadAlloc = e.toString().contains('bad_alloc') ||
(e is WalletOpeningException &&
(e.message == 'std::bad_alloc' || e.message.contains('bad_alloc')));
final bool doesNotCorrespond = e.toString().contains('does not correspond') ||
(e is WalletOpeningException && e.message.contains('does not correspond'));
final bool isMissingCacheFilesIOS = e.toString().contains('basic_string') ||
(e is WalletOpeningException && e.message.contains('basic_string'));
final bool isMissingCacheFilesAndroid = e.toString().contains('input_stream') ||
e.toString().contains('input stream error') ||
(e is WalletOpeningException &&
(e.message.contains('input_stream') || e.message.contains('input stream error')));
final bool invalidSignature = e.toString().contains('invalid signature') ||
(e is WalletOpeningException && e.message.contains('invalid signature'));
final bool invalidPassword = e.toString().contains('invalid password') ||
(e is WalletOpeningException && e.message.contains('invalid password'));
if (!isBadAlloc &&
!doesNotCorrespond &&
!isMissingCacheFilesIOS &&
!isMissingCacheFilesAndroid &&
!invalidSignature &&
!invalidPassword &&
wallet != null &&
wallet.onError != null) {
wallet.onError!(FlutterErrorDetails(exception: e, stack: s));
}
if (invalidPassword || retryOnFailure == false) {
if (retryOnFailure == false) {
rethrow;
}

View file

@ -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"

View file

@ -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

View file

@ -466,6 +466,7 @@ class NanoClient {
blocks = blocks as Map<String, dynamic>;
try {
// confirm all receivable blocks:
for (final blockHash in blocks.keys) {
final block = blocks[blockHash];
@ -479,8 +480,11 @@ class NanoClient {
// 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;
}
}
void stop() {}

View file

@ -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();

View file

@ -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"

View file

@ -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

View file

@ -29,8 +29,9 @@ class CWBitcoin extends Bitcoin {
@override
WalletCredentials createBitcoinNewWalletCredentials(
{required String name, WalletInfo? walletInfo, String? password}) =>
BitcoinNewWalletCredentials(name: name, walletInfo: walletInfo, password: password);
{required String name, WalletInfo? walletInfo, String? password, String? passphrase}) =>
BitcoinNewWalletCredentials(
name: name, walletInfo: walletInfo, password: password, passphrase: passphrase);
@override
WalletCredentials createBitcoinHardwareWalletCredentials(
@ -202,8 +203,8 @@ class CWBitcoin extends Bitcoin {
await bitcoinWallet.updateAllUnspents();
}
WalletService createBitcoinWalletService(
Box<WalletInfo> walletInfoSource, Box<UnspentCoinsInfo> unspentCoinSource, bool alwaysScan, bool isDirect) {
WalletService createBitcoinWalletService(Box<WalletInfo> walletInfoSource,
Box<UnspentCoinsInfo> unspentCoinSource, bool alwaysScan, bool isDirect) {
return BitcoinWalletService(walletInfoSource, unspentCoinSource, alwaysScan, isDirect);
}
@ -315,7 +316,7 @@ class CWBitcoin extends Bitcoin {
for (DerivationType dType in electrum_derivations.keys) {
late Uint8List seedBytes;
if (dType == DerivationType.electrum) {
seedBytes = await mnemonicToSeedBytes(mnemonic);
seedBytes = await mnemonicToSeedBytes(mnemonic, passphrase: passphrase ?? "");
} else if (dType == DerivationType.bip39) {
seedBytes = bip39.mnemonicToSeed(mnemonic, passphrase: passphrase ?? '');
}
@ -398,9 +399,10 @@ class CWBitcoin extends Bitcoin {
}
@override
Future<bool> canReplaceByFee(Object wallet, String transactionHash) async {
Future<bool> canReplaceByFee(Object wallet, Object transactionInfo) async {
final bitcoinWallet = wallet as ElectrumWallet;
return bitcoinWallet.canReplaceByFee(transactionHash);
final tx = transactionInfo as ElectrumTransactionInfo;
return bitcoinWallet.canReplaceByFee(tx);
}
@override
@ -516,7 +518,7 @@ class CWBitcoin extends Bitcoin {
@override
bool isTestnet(Object wallet) {
final bitcoinWallet = wallet as ElectrumWallet;
return bitcoinWallet.isTestnet ?? false;
return bitcoinWallet.isTestnet;
}
@override

View file

@ -15,14 +15,16 @@ class CWBitcoinCash extends BitcoinCash {
required String name,
WalletInfo? walletInfo,
String? password,
String? passphrase,
}) =>
BitcoinCashNewWalletCredentials(name: name, walletInfo: walletInfo, password: password);
BitcoinCashNewWalletCredentials(
name: name, walletInfo: walletInfo, password: password, passphrase: passphrase);
@override
WalletCredentials createBitcoinCashRestoreWalletFromSeedCredentials(
{required String name, required String mnemonic, required String password}) =>
{required String name, required String mnemonic, required String password, String? passphrase}) =>
BitcoinCashRestoreWalletFromSeedCredentials(
name: name, mnemonic: mnemonic, password: password);
name: name, mnemonic: mnemonic, password: password, passphrase: passphrase);
@override
TransactionPriority deserializeBitcoinCashTransactionPriority(int raw) =>

View file

@ -75,8 +75,10 @@ class WalletCreationService {
bool get _hasSeedPhraseLengthOption {
switch (type) {
case WalletType.ethereum:
case WalletType.bitcoin:
case WalletType.litecoin:
case WalletType.bitcoinCash:
case WalletType.ethereum:
case WalletType.polygon:
case WalletType.solana:
case WalletType.tron:
@ -84,8 +86,6 @@ class WalletCreationService {
case WalletType.monero:
case WalletType.wownero:
case WalletType.none:
case WalletType.bitcoin:
case WalletType.litecoin:
case WalletType.haven:
case WalletType.nano:
case WalletType.banano:

View file

@ -146,7 +146,7 @@ import 'package:cake_wallet/view_model/cake_pay/cake_pay_purchase_view_model.dar
import 'package:cake_wallet/view_model/nano_account_list/nano_account_edit_or_create_view_model.dart';
import 'package:cake_wallet/view_model/nano_account_list/nano_account_list_view_model.dart';
import 'package:cake_wallet/view_model/node_list/pow_node_list_view_model.dart';
import 'package:cake_wallet/view_model/seed_type_view_model.dart';
import 'package:cake_wallet/view_model/seed_settings_view_model.dart';
import 'package:cake_wallet/view_model/set_up_2fa_viewmodel.dart';
import 'package:cake_wallet/view_model/restore/restore_from_qr_vm.dart';
import 'package:cake_wallet/view_model/settings/display_settings_view_model.dart';
@ -179,6 +179,7 @@ import 'package:cake_wallet/store/dashboard/trades_store.dart';
import 'package:cake_wallet/store/dashboard/transaction_filter_store.dart';
import 'package:cake_wallet/store/node_list_store.dart';
import 'package:cake_wallet/store/secret_store.dart';
import 'package:cake_wallet/store/seed_settings_store.dart';
import 'package:cake_wallet/store/settings_store.dart';
import 'package:cake_wallet/store/templates/exchange_template_store.dart';
import 'package:cake_wallet/store/templates/send_template_store.dart';
@ -331,6 +332,7 @@ Future<void> setup({
YatStore(appStore: getIt.get<AppStore>(), secureStorage: getIt.get<SecureStorage>())..init());
getIt.registerSingleton<AnonpayTransactionsStore>(
AnonpayTransactionsStore(anonpayInvoiceInfoSource: _anonpayInvoiceInfoSource));
getIt.registerSingleton<SeedSettingsStore>(SeedSettingsStore());
getIt.registerLazySingleton(() => LedgerViewModel());
@ -361,6 +363,7 @@ Future<void> setup({
getIt.get<WalletCreationService>(param1: type),
_walletInfoSource,
getIt.get<AdvancedPrivacySettingsViewModel>(param1: type),
getIt.get<SeedSettingsViewModel>(),
type: type));
getIt.registerFactoryParam<WalletUnlockPage, WalletUnlockArguments, bool>((args, closable) {
@ -422,14 +425,21 @@ Future<void> setup({
walletType: args.walletType ?? currentWalletType);
});
getIt.registerFactoryParam<WalletRestorationFromQRVM, WalletType, void>((WalletType type, _) {
return WalletRestorationFromQRVM(getIt.get<AppStore>(),
getIt.get<WalletCreationService>(param1: type), _walletInfoSource, type);
});
getIt.registerFactoryParam<WalletRestorationFromQRVM, WalletType, void>((WalletType type, _) =>
WalletRestorationFromQRVM(
getIt.get<AppStore>(),
getIt.get<WalletCreationService>(param1: type),
_walletInfoSource,
type,
getIt.get<SeedSettingsViewModel>()));
getIt.registerFactoryParam<WalletHardwareRestoreViewModel, WalletType, void>((type, _) =>
WalletHardwareRestoreViewModel(getIt.get<LedgerViewModel>(), getIt.get<AppStore>(),
getIt.get<WalletCreationService>(param1: type), _walletInfoSource,
WalletHardwareRestoreViewModel(
getIt.get<LedgerViewModel>(),
getIt.get<AppStore>(),
getIt.get<WalletCreationService>(param1: type),
_walletInfoSource,
getIt.get<SeedSettingsViewModel>(),
type: type));
getIt.registerFactory<WalletAddressListViewModel>(() => WalletAddressListViewModel(
@ -833,7 +843,7 @@ Future<void> setup({
getIt.registerFactory(() => WalletSeedViewModel(getIt.get<AppStore>().wallet!));
getIt.registerFactory<SeedTypeViewModel>(() => SeedTypeViewModel(getIt.get<AppStore>()));
getIt.registerFactory<SeedSettingsViewModel>(() => SeedSettingsViewModel(getIt.get<AppStore>(), getIt.get<SeedSettingsStore>()));
getIt.registerFactoryParam<WalletSeedPage, bool, void>((bool isWalletCreated, _) =>
WalletSeedPage(getIt.get<WalletSeedViewModel>(), isNewWalletCreated: isWalletCreated));
@ -1018,12 +1028,12 @@ Future<void> setup({
getIt.registerFactory(() => FaqPage(getIt.get<SettingsStore>()));
getIt.registerFactoryParam<WalletRestoreViewModel, WalletType, void>((type, _) =>
WalletRestoreViewModel(
getIt.get<AppStore>(), getIt.get<WalletCreationService>(param1: type), _walletInfoSource,
WalletRestoreViewModel(getIt.get<AppStore>(), getIt.get<WalletCreationService>(param1: type),
_walletInfoSource, getIt.get<SeedSettingsViewModel>(),
type: type));
getIt.registerFactoryParam<WalletRestorePage, WalletType, void>((type, _) => WalletRestorePage(
getIt.get<WalletRestoreViewModel>(param1: type), getIt.get<SeedTypeViewModel>()));
getIt.get<WalletRestoreViewModel>(param1: type), getIt.get<SeedSettingsViewModel>()));
getIt.registerFactoryParam<WalletRestoreChooseDerivationViewModel, List<DerivationInfo>, void>(
(derivations, _) => WalletRestoreChooseDerivationViewModel(derivationInfos: derivations));

View file

@ -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);
}
}

View file

@ -29,7 +29,9 @@ class LanguageService {
'id': 'Bahasa Indonesia (Indonesian)',
'yo': 'Yorùbá (Yoruba)',
'ha': 'Hausa Najeriya (Nigeria)',
'tl': 'Filipino (Tagalog)'
'tl': 'Filipino (Tagalog)',
'hy': 'Հայերեն (Armenian)',
'vi': 'Tiếng Việt (Vietnamese)',
};
static const Map<String, String> localeCountryCode = {
@ -58,7 +60,9 @@ class LanguageService {
'id': 'idn',
'yo': 'nga',
'ha': 'hau',
'tl': 'phl'
'tl': 'phl',
'hy': 'arm',
'vi': 'vnm',
};
static final list = <String, String>{};

View file

@ -61,6 +61,7 @@ class PreferencesKey {
static const useEtherscan = 'use_etherscan';
static const usePolygonScan = 'use_polygonscan';
static const useTronGrid = 'use_trongrid';
static const useMempoolFeeAPI = 'use_mempool_fee_api';
static const defaultNanoRep = 'default_nano_representative';
static const defaultBananoRep = 'default_banano_representative';
static const lookupsTwitter = 'looks_up_twitter';
@ -77,6 +78,7 @@ class PreferencesKey {
static const exchangeProvidersSelection = 'exchange-providers-selection';
static const autoGenerateSubaddressStatusKey = 'auto_generate_subaddress_status';
static const moneroSeedType = 'monero_seed_type';
static const bitcoinSeedType = 'bitcoin_seed_type';
static const clearnetDonationLink = 'clearnet_donation_link';
static const onionDonationLink = 'onion_donation_link';
static const donationLinkWalletName = 'donation_link_wallet_name';

Some files were not shown because too many files have changed in this diff Show more