feat: add flatpak build variable to use different default storage location to avoid needing filesystem=home permission

This commit is contained in:
Rafael Saes 2024-01-30 14:20:10 -03:00
parent 67a96807d1
commit 54be0f863c
44 changed files with 1014 additions and 702 deletions

7
.gitignore vendored
View file

@ -158,3 +158,10 @@ macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png
macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png
macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png
macos/Runner/Configs/AppInfo.xcconfig macos/Runner/Configs/AppInfo.xcconfig
# Linux flatpak build related
.flatpak-builder/
export/
cake_wallet.flatpak
lib/core/flatpak.dart

View file

@ -27,16 +27,16 @@ CakeWallet requires some packages to be install on your build system. You may ea
> To check what gcc version you are using: > To check what gcc version you are using:
> >
> ```bash > ```bash
> $ gcc --version > gcc --version
> $ g++ --version > g++ --version
> ``` > ```
> >
> If you are using gcc version newer than 10, then you need to downgrade to version 10.4.0: > If you are using gcc version newer than 10, then you need to downgrade to version 10.4.0:
> >
> ```bash > ```bash
> $ sudo apt install gcc-10 g++-10 > sudo apt install gcc-10 g++-10
> $ sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-10 10 > sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-10 10
> $ sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-10 10 > sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-10 10
> ``` > ```
> [!NOTE] > [!NOTE]
@ -44,7 +44,7 @@ CakeWallet requires some packages to be install on your build system. You may ea
> Alternatively, you can use the [nix-shell](https://nixos.org/) with the `gcc10.nix` file\ > Alternatively, you can use the [nix-shell](https://nixos.org/) with the `gcc10.nix` file\
> present on `scripts/linux` like so: > present on `scripts/linux` like so:
> ```bash > ```bash
> $ nix-shell gcc10.nix > nix-shell gcc10.nix
> ``` > ```
> This will get you in a nix environment with all the required dependencies that you can use to build the software from,\ > This will get you in a nix environment with all the required dependencies that you can use to build the software from,\
> and it works in any linux distro. > and it works in any linux distro.
@ -145,31 +145,38 @@ Path to executable file will be:
# Flatpak # Flatpak
For package the built application into flatpak you need fistly to install `flatpak` and `flatpak-builder`: > [!NOTE]
>
> To package the built application into flatpak, you will need to follow the steps above
> but replace the `$ source ./app_env.sh cakewallet` step with `$ source ./app_env.sh cakewallet-flatpak` first.
Install `flatpak` and `flatpak-builder`:
`$ sudo apt install flatpak flatpak-builder` `$ sudo apt install flatpak flatpak-builder`
Then need to [add flathub](https://flatpak.org/setup/Ubuntu) (or just `$ flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo`). Then need to install freedesktop runtime and sdk: Then need to [add flathub](https://flatpak.org/setup/Ubuntu) (or just `$ flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo`).
Install the freedesktop runtime and sdk:
`$ flatpak install flathub org.freedesktop.Platform//22.08 org.freedesktop.Sdk//22.08` `$ flatpak install flathub org.freedesktop.Platform//23.08 org.freedesktop.Sdk//23.08`
To build with using of `flatpak-build` directory run next: Next, to build using `flatpak-build` run:
`$ flatpak-builder --force-clean flatpak-build com.cakewallet.CakeWallet.yml` `$ flatpak-builder --force-clean flatpak-build com.cakewallet.CakeWallet.yml`
And then export bundle: And then export the bundle:
`$ flatpak build-export export flatpak-build` ```bash
flatpak build-export export flatpak-build
flatpak build-bundle export cake_wallet.flatpak com.cakewallet.CakeWallet
```
`$ flatpak build-bundle export cake_wallet.flatpak com.cakewallet.CakeWallet` Result file: `cake_wallet.flatpak` should be generated in the current directory.
Result file: `cake_wallet.flatpak` should be generated in current directory. To install the generated flatpak file use:
For install generated flatpak file use:
`$ flatpak --user install cake_wallet.flatpak` `$ flatpak --user install cake_wallet.flatpak`
For run the installed application run: To run the installed application run:
`$ flatpak run com.cakewallet.CakeWallet` `$ flatpak run com.cakewallet.CakeWallet`

View file

@ -1,6 +1,6 @@
app-id: com.cakewallet.CakeWallet app-id: com.cakewallet.CakeWallet
runtime: org.freedesktop.Platform runtime: org.freedesktop.Platform
runtime-version: '22.08' runtime-version: '23.08'
sdk: org.freedesktop.Sdk sdk: org.freedesktop.Sdk
command: cake_wallet command: cake_wallet
separate-locales: false separate-locales: false
@ -11,7 +11,6 @@ finish-args:
- --device=dri - --device=dri
- --socket=pulseaudio - --socket=pulseaudio
- --share=network - --share=network
- --filesystem=home
modules: modules:
- name: cake_wallet - name: cake_wallet
buildsystem: simple buildsystem: simple

View file

@ -18,37 +18,37 @@ part 'bitcoin_wallet.g.dart';
class BitcoinWallet = BitcoinWalletBase with _$BitcoinWallet; class BitcoinWallet = BitcoinWalletBase with _$BitcoinWallet;
abstract class BitcoinWalletBase extends ElectrumWallet with Store { abstract class BitcoinWalletBase extends ElectrumWallet with Store {
BitcoinWalletBase( BitcoinWalletBase({
{required String mnemonic, required String mnemonic,
required String password, required String password,
required WalletInfo walletInfo, required WalletInfo walletInfo,
required Box<UnspentCoinsInfo> unspentCoinsInfo, required Box<UnspentCoinsInfo> unspentCoinsInfo,
required Uint8List seedBytes, required Uint8List seedBytes,
required EncryptionFileUtils encryptionFileUtils, required EncryptionFileUtils encryptionFileUtils,
List<BitcoinAddressRecord>? initialAddresses, List<BitcoinAddressRecord>? initialAddresses,
ElectrumBalance? initialBalance, ElectrumBalance? initialBalance,
int initialRegularAddressIndex = 0, int initialRegularAddressIndex = 0,
int initialChangeAddressIndex = 0}) int initialChangeAddressIndex = 0,
: super( required super.isFlatpak,
mnemonic: mnemonic, }) : super(
password: password, mnemonic: mnemonic,
walletInfo: walletInfo, password: password,
unspentCoinsInfo: unspentCoinsInfo, walletInfo: walletInfo,
networkType: bitcoin.bitcoin, unspentCoinsInfo: unspentCoinsInfo,
initialAddresses: initialAddresses, networkType: bitcoin.bitcoin,
initialBalance: initialBalance, initialAddresses: initialAddresses,
seedBytes: seedBytes, initialBalance: initialBalance,
currency: CryptoCurrency.btc, seedBytes: seedBytes,
encryptionFileUtils: encryptionFileUtils) { currency: CryptoCurrency.btc,
walletAddresses = BitcoinWalletAddresses( encryptionFileUtils: encryptionFileUtils,
walletInfo, ) {
walletAddresses = BitcoinWalletAddresses(walletInfo,
electrumClient: electrumClient, electrumClient: electrumClient,
initialAddresses: initialAddresses, initialAddresses: initialAddresses,
initialRegularAddressIndex: initialRegularAddressIndex, initialRegularAddressIndex: initialRegularAddressIndex,
initialChangeAddressIndex: initialChangeAddressIndex, initialChangeAddressIndex: initialChangeAddressIndex,
mainHd: hd, mainHd: hd,
sideHd: bitcoin.HDWallet.fromSeed(seedBytes, network: networkType) sideHd: bitcoin.HDWallet.fromSeed(seedBytes, network: networkType).derivePath("m/0'/1"),
.derivePath("m/0'/1"),
networkType: networkType); networkType: networkType);
autorun((_) { autorun((_) {
this.walletAddresses.isEnabledAutoGenerateSubaddress = this.isEnabledAutoGenerateSubaddress; this.walletAddresses.isEnabledAutoGenerateSubaddress = this.isEnabledAutoGenerateSubaddress;
@ -64,19 +64,22 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
List<BitcoinAddressRecord>? initialAddresses, List<BitcoinAddressRecord>? initialAddresses,
ElectrumBalance? initialBalance, ElectrumBalance? initialBalance,
int initialRegularAddressIndex = 0, int initialRegularAddressIndex = 0,
int initialChangeAddressIndex = 0 int initialChangeAddressIndex = 0,
required bool isFlatpak,
}) async { }) async {
return BitcoinWallet( return BitcoinWallet(
mnemonic: mnemonic, mnemonic: mnemonic,
password: password, password: password,
walletInfo: walletInfo, walletInfo: walletInfo,
unspentCoinsInfo: unspentCoinsInfo, unspentCoinsInfo: unspentCoinsInfo,
initialAddresses: initialAddresses, initialAddresses: initialAddresses,
initialBalance: initialBalance, initialBalance: initialBalance,
encryptionFileUtils: encryptionFileUtils, encryptionFileUtils: encryptionFileUtils,
seedBytes: await mnemonicToSeedBytes(mnemonic), seedBytes: await mnemonicToSeedBytes(mnemonic),
initialRegularAddressIndex: initialRegularAddressIndex, initialRegularAddressIndex: initialRegularAddressIndex,
initialChangeAddressIndex: initialChangeAddressIndex); initialChangeAddressIndex: initialChangeAddressIndex,
isFlatpak: isFlatpak,
);
} }
static Future<BitcoinWallet> open({ static Future<BitcoinWallet> open({
@ -85,18 +88,23 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
required Box<UnspentCoinsInfo> unspentCoinsInfo, required Box<UnspentCoinsInfo> unspentCoinsInfo,
required String password, required String password,
required EncryptionFileUtils encryptionFileUtils, required EncryptionFileUtils encryptionFileUtils,
required bool isFlatpak,
}) async { }) async {
final snp = await ElectrumWallletSnapshot.load(encryptionFileUtils, name, walletInfo.type, password); final snp = await ElectrumWallletSnapshot.load(
encryptionFileUtils, name, walletInfo.type, password, isFlatpak);
return BitcoinWallet( return BitcoinWallet(
mnemonic: snp.mnemonic, mnemonic: snp.mnemonic,
password: password, password: password,
walletInfo: walletInfo, walletInfo: walletInfo,
unspentCoinsInfo: unspentCoinsInfo, unspentCoinsInfo: unspentCoinsInfo,
initialAddresses: snp.addresses, initialAddresses: snp.addresses,
initialBalance: snp.balance, initialBalance: snp.balance,
seedBytes: await mnemonicToSeedBytes(snp.mnemonic), seedBytes: await mnemonicToSeedBytes(snp.mnemonic),
encryptionFileUtils: encryptionFileUtils, encryptionFileUtils: encryptionFileUtils,
initialRegularAddressIndex: snp.regularAddressIndex, initialRegularAddressIndex: snp.regularAddressIndex,
initialChangeAddressIndex: snp.changeAddressIndex); initialChangeAddressIndex: snp.changeAddressIndex,
isFlatpak: isFlatpak,
);
} }
} }

View file

@ -13,15 +13,15 @@ import 'package:cw_core/wallet_type.dart';
import 'package:hive/hive.dart'; import 'package:hive/hive.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
class BitcoinWalletService extends WalletService< class BitcoinWalletService extends WalletService<BitcoinNewWalletCredentials,
BitcoinNewWalletCredentials, BitcoinRestoreWalletFromSeedCredentials, BitcoinRestoreWalletFromWIFCredentials> {
BitcoinRestoreWalletFromSeedCredentials, BitcoinWalletService(
BitcoinRestoreWalletFromWIFCredentials> { this.walletInfoSource, this.unspentCoinsInfoSource, this.isDirect, this.isFlatpak);
BitcoinWalletService(this.walletInfoSource, this.unspentCoinsInfoSource, this.isDirect);
final Box<WalletInfo> walletInfoSource; final Box<WalletInfo> walletInfoSource;
final Box<UnspentCoinsInfo> unspentCoinsInfoSource; final Box<UnspentCoinsInfo> unspentCoinsInfoSource;
final bool isDirect; final bool isDirect;
final bool isFlatpak;
@override @override
WalletType getType() => WalletType.bitcoin; WalletType getType() => WalletType.bitcoin;
@ -29,11 +29,13 @@ class BitcoinWalletService extends WalletService<
@override @override
Future<BitcoinWallet> create(BitcoinNewWalletCredentials credentials) async { Future<BitcoinWallet> create(BitcoinNewWalletCredentials credentials) async {
final wallet = await BitcoinWalletBase.create( final wallet = await BitcoinWalletBase.create(
mnemonic: await generateMnemonic(), mnemonic: await generateMnemonic(),
password: credentials.password!, password: credentials.password!,
walletInfo: credentials.walletInfo!, walletInfo: credentials.walletInfo!,
unspentCoinsInfo: unspentCoinsInfoSource, unspentCoinsInfo: unspentCoinsInfoSource,
encryptionFileUtils: encryptionFileUtilsFor(isDirect)); encryptionFileUtils: encryptionFileUtilsFor(isDirect),
isFlatpak: isFlatpak,
);
await wallet.save(); await wallet.save();
await wallet.init(); await wallet.init();
return wallet; return wallet;
@ -41,41 +43,45 @@ class BitcoinWalletService extends WalletService<
@override @override
Future<bool> isWalletExit(String name) async => Future<bool> isWalletExit(String name) async =>
File(await pathForWallet(name: name, type: getType())).existsSync(); File(await pathForWallet(name: name, type: getType(), isFlatpak: isFlatpak)).existsSync();
@override @override
Future<BitcoinWallet> openWallet(String name, String password) async { Future<BitcoinWallet> openWallet(String name, String password) async {
final walletInfo = walletInfoSource.values.firstWhereOrNull( final walletInfo = walletInfoSource.values
(info) => info.id == WalletBase.idFor(name, getType()))!; .firstWhereOrNull((info) => info.id == WalletBase.idFor(name, getType()))!;
final wallet = await BitcoinWalletBase.open( final wallet = await BitcoinWalletBase.open(
password: password, password: password,
name: name, name: name,
walletInfo: walletInfo, walletInfo: walletInfo,
unspentCoinsInfo: unspentCoinsInfoSource, unspentCoinsInfo: unspentCoinsInfoSource,
encryptionFileUtils: encryptionFileUtilsFor(isDirect)); encryptionFileUtils: encryptionFileUtilsFor(isDirect),
isFlatpak: isFlatpak,
);
await wallet.init(); await wallet.init();
return wallet; return wallet;
} }
@override @override
Future<void> remove(String wallet) async { Future<void> remove(String wallet) async {
File(await pathForWalletDir(name: wallet, type: getType())) File(await pathForWalletDir(name: wallet, type: getType(), isFlatpak: isFlatpak))
.delete(recursive: true); .delete(recursive: true);
final walletInfo = walletInfoSource.values.firstWhereOrNull( final walletInfo = walletInfoSource.values
(info) => info.id == WalletBase.idFor(wallet, getType()))!; .firstWhereOrNull((info) => info.id == WalletBase.idFor(wallet, getType()))!;
await walletInfoSource.delete(walletInfo.key); await walletInfoSource.delete(walletInfo.key);
} }
@override @override
Future<void> rename(String currentName, String password, String newName) async { Future<void> rename(String currentName, String password, String newName) async {
final currentWalletInfo = walletInfoSource.values.firstWhereOrNull( final currentWalletInfo = walletInfoSource.values
(info) => info.id == WalletBase.idFor(currentName, getType()))!; .firstWhereOrNull((info) => info.id == WalletBase.idFor(currentName, getType()))!;
final currentWallet = await BitcoinWalletBase.open( final currentWallet = await BitcoinWalletBase.open(
password: password, password: password,
name: currentName, name: currentName,
walletInfo: currentWalletInfo, walletInfo: currentWalletInfo,
unspentCoinsInfo: unspentCoinsInfoSource, unspentCoinsInfo: unspentCoinsInfoSource,
encryptionFileUtils: encryptionFileUtilsFor(isDirect)); encryptionFileUtils: encryptionFileUtilsFor(isDirect),
isFlatpak: isFlatpak,
);
await currentWallet.renameWalletFiles(newName); await currentWallet.renameWalletFiles(newName);
@ -87,23 +93,23 @@ class BitcoinWalletService extends WalletService<
} }
@override @override
Future<BitcoinWallet> restoreFromKeys( Future<BitcoinWallet> restoreFromKeys(BitcoinRestoreWalletFromWIFCredentials credentials) async =>
BitcoinRestoreWalletFromWIFCredentials credentials) async =>
throw UnimplementedError(); throw UnimplementedError();
@override @override
Future<BitcoinWallet> restoreFromSeed( Future<BitcoinWallet> restoreFromSeed(BitcoinRestoreWalletFromSeedCredentials credentials) async {
BitcoinRestoreWalletFromSeedCredentials credentials) async {
if (!validateMnemonic(credentials.mnemonic)) { if (!validateMnemonic(credentials.mnemonic)) {
throw BitcoinMnemonicIsIncorrectException(); throw BitcoinMnemonicIsIncorrectException();
} }
final wallet = await BitcoinWalletBase.create( final wallet = await BitcoinWalletBase.create(
password: credentials.password!, password: credentials.password!,
mnemonic: credentials.mnemonic, mnemonic: credentials.mnemonic,
walletInfo: credentials.walletInfo!, walletInfo: credentials.walletInfo!,
unspentCoinsInfo: unspentCoinsInfoSource, unspentCoinsInfo: unspentCoinsInfoSource,
encryptionFileUtils: encryptionFileUtilsFor(isDirect)); encryptionFileUtils: encryptionFileUtilsFor(isDirect),
isFlatpak: isFlatpak,
);
await wallet.save(); await wallet.save();
await wallet.init(); await wallet.init();
return wallet; return wallet;

View file

@ -4,23 +4,22 @@ import 'package:cw_core/encryption_file_utils.dart';
import 'package:cw_bitcoin/electrum_transaction_info.dart'; import 'package:cw_bitcoin/electrum_transaction_info.dart';
import 'package:cw_core/pathForWallet.dart'; import 'package:cw_core/pathForWallet.dart';
import 'package:cw_core/transaction_history.dart'; import 'package:cw_core/transaction_history.dart';
import 'package:cw_core/utils/file.dart';
import 'package:cw_core/wallet_info.dart'; import 'package:cw_core/wallet_info.dart';
import 'package:mobx/mobx.dart'; import 'package:mobx/mobx.dart';
import 'package:cw_core/transaction_history.dart';
import 'package:cw_bitcoin/electrum_transaction_info.dart';
part 'electrum_transaction_history.g.dart'; part 'electrum_transaction_history.g.dart';
const transactionsHistoryFileName = 'transactions.json'; const transactionsHistoryFileName = 'transactions.json';
class ElectrumTransactionHistory = ElectrumTransactionHistoryBase class ElectrumTransactionHistory = ElectrumTransactionHistoryBase with _$ElectrumTransactionHistory;
with _$ElectrumTransactionHistory;
abstract class ElectrumTransactionHistoryBase abstract class ElectrumTransactionHistoryBase
extends TransactionHistoryBase<ElectrumTransactionInfo> with Store { extends TransactionHistoryBase<ElectrumTransactionInfo> with Store {
ElectrumTransactionHistoryBase( ElectrumTransactionHistoryBase(
{required this.walletInfo, required String password, required this.encryptionFileUtils}) {required this.walletInfo,
required String password,
required this.encryptionFileUtils,
required this.isFlatpak})
: _password = password, : _password = password,
_height = 0 { _height = 0 {
transactions = ObservableMap<String, ElectrumTransactionInfo>(); transactions = ObservableMap<String, ElectrumTransactionInfo>();
@ -28,14 +27,14 @@ abstract class ElectrumTransactionHistoryBase
final WalletInfo walletInfo; final WalletInfo walletInfo;
final EncryptionFileUtils encryptionFileUtils; final EncryptionFileUtils encryptionFileUtils;
final bool isFlatpak;
String _password; String _password;
int _height; int _height;
Future<void> init() async => await _load(); Future<void> init() async => await _load();
@override @override
void addOne(ElectrumTransactionInfo transaction) => void addOne(ElectrumTransactionInfo transaction) => transactions[transaction.id] = transaction;
transactions[transaction.id] = transaction;
@override @override
void addMany(Map<String, ElectrumTransactionInfo> transactions) => void addMany(Map<String, ElectrumTransactionInfo> transactions) =>
@ -44,11 +43,10 @@ abstract class ElectrumTransactionHistoryBase
@override @override
Future<void> save() async { Future<void> save() async {
try { try {
final dirPath = final dirPath = await pathForWalletDir(
await pathForWalletDir(name: walletInfo.name, type: walletInfo.type); name: walletInfo.name, type: walletInfo.type, isFlatpak: isFlatpak);
final path = '$dirPath/$transactionsHistoryFileName'; final path = '$dirPath/$transactionsHistoryFileName';
final data = final data = json.encode({'height': _height, 'transactions': transactions});
json.encode({'height': _height, 'transactions': transactions});
await encryptionFileUtils.write(path: path, password: _password, data: data); await encryptionFileUtils.write(path: path, password: _password, data: data);
} catch (e) { } catch (e) {
print('Error while save bitcoin transaction history: ${e.toString()}'); print('Error while save bitcoin transaction history: ${e.toString()}');
@ -62,7 +60,7 @@ abstract class ElectrumTransactionHistoryBase
Future<Map<String, dynamic>> _read() async { Future<Map<String, dynamic>> _read() async {
final dirPath = final dirPath =
await pathForWalletDir(name: walletInfo.name, type: walletInfo.type); await pathForWalletDir(name: walletInfo.name, type: walletInfo.type, isFlatpak: isFlatpak);
final path = '$dirPath/$transactionsHistoryFileName'; final path = '$dirPath/$transactionsHistoryFileName';
final content = await encryptionFileUtils.read(path: path, password: _password); final content = await encryptionFileUtils.read(path: path, password: _password);
return json.decode(content) as Map<String, dynamic>; return json.decode(content) as Map<String, dynamic>;
@ -88,7 +86,5 @@ abstract class ElectrumTransactionHistoryBase
} }
} }
void _update(ElectrumTransactionInfo transaction) => void _update(ElectrumTransactionInfo transaction) => transactions[transaction.id] = transaction;
transactions[transaction.id] = transaction;
} }

View file

@ -48,16 +48,17 @@ abstract class ElectrumWalletBase
with Store { with Store {
ElectrumWalletBase( ElectrumWalletBase(
{required String password, {required String password,
required WalletInfo walletInfo, required WalletInfo walletInfo,
required Box<UnspentCoinsInfo> unspentCoinsInfo, required Box<UnspentCoinsInfo> unspentCoinsInfo,
required this.networkType, required this.networkType,
required this.mnemonic, required this.mnemonic,
required Uint8List seedBytes, required Uint8List seedBytes,
required this.encryptionFileUtils, required this.encryptionFileUtils,
List<BitcoinAddressRecord>? initialAddresses, required this.isFlatpak,
ElectrumClient? electrumClient, List<BitcoinAddressRecord>? initialAddresses,
ElectrumBalance? initialBalance, ElectrumClient? electrumClient,
CryptoCurrency? currency}) ElectrumBalance? initialBalance,
CryptoCurrency? currency})
: hd = currency == CryptoCurrency.bch : hd = currency == CryptoCurrency.bch
? bitcoinCashHDWallet(seedBytes) ? bitcoinCashHDWallet(seedBytes)
: bitcoin.HDWallet.fromSeed(seedBytes, network: networkType).derivePath("m/0'/0"), : bitcoin.HDWallet.fromSeed(seedBytes, network: networkType).derivePath("m/0'/0"),
@ -78,13 +79,15 @@ abstract class ElectrumWalletBase
super(walletInfo) { super(walletInfo) {
this.electrumClient = electrumClient ?? ElectrumClient(); this.electrumClient = electrumClient ?? ElectrumClient();
this.walletInfo = walletInfo; this.walletInfo = walletInfo;
transactionHistory = transactionHistory = ElectrumTransactionHistory(
ElectrumTransactionHistory( walletInfo: walletInfo,
walletInfo: walletInfo, password: password,
password: password, encryptionFileUtils: encryptionFileUtils,
encryptionFileUtils: encryptionFileUtils); isFlatpak: isFlatpak);
} }
final bool isFlatpak;
static bitcoin.HDWallet bitcoinCashHDWallet(Uint8List seedBytes) => static bitcoin.HDWallet bitcoinCashHDWallet(Uint8List seedBytes) =>
bitcoin.HDWallet.fromSeed(seedBytes).derivePath("m/44'/145'/0'/0"); bitcoin.HDWallet.fromSeed(seedBytes).derivePath("m/44'/145'/0'/0");
@ -440,19 +443,23 @@ abstract class ElectrumWalletBase
@override @override
Future<void> renameWalletFiles(String newWalletName) async { Future<void> renameWalletFiles(String newWalletName) async {
final currentWalletPath = await pathForWallet(name: walletInfo.name, type: type); final currentWalletPath =
await pathForWallet(name: walletInfo.name, type: type, isFlatpak: isFlatpak);
final currentWalletFile = File(currentWalletPath); final currentWalletFile = File(currentWalletPath);
final currentDirPath = await pathForWalletDir(name: walletInfo.name, type: type); final currentDirPath =
await pathForWalletDir(name: walletInfo.name, type: type, isFlatpak: isFlatpak);
final currentTransactionsFile = File('$currentDirPath/$transactionsHistoryFileName'); final currentTransactionsFile = File('$currentDirPath/$transactionsHistoryFileName');
// Copies current wallet files into new wallet name's dir and files // Copies current wallet files into new wallet name's dir and files
if (currentWalletFile.existsSync()) { if (currentWalletFile.existsSync()) {
final newWalletPath = await pathForWallet(name: newWalletName, type: type); final newWalletPath =
await pathForWallet(name: newWalletName, type: type, isFlatpak: isFlatpak);
await currentWalletFile.copy(newWalletPath); await currentWalletFile.copy(newWalletPath);
} }
if (currentTransactionsFile.existsSync()) { if (currentTransactionsFile.existsSync()) {
final newDirPath = await pathForWalletDir(name: newWalletName, type: type); final newDirPath =
await pathForWalletDir(name: newWalletName, type: type, isFlatpak: isFlatpak);
await currentTransactionsFile.copy('$newDirPath/$transactionsHistoryFileName'); await currentTransactionsFile.copy('$newDirPath/$transactionsHistoryFileName');
} }
@ -480,7 +487,8 @@ abstract class ElectrumWalletBase
} catch (_) {} } catch (_) {}
} }
Future<String> makePath() async => pathForWallet(name: walletInfo.name, type: walletInfo.type); Future<String> makePath() async =>
pathForWallet(name: walletInfo.name, type: walletInfo.type, isFlatpak: isFlatpak);
Future<void> updateUnspent() async { Future<void> updateUnspent() async {
final unspent = await Future.wait(walletAddresses.addresses.map((address) => electrumClient final unspent = await Future.wait(walletAddresses.addresses.map((address) => electrumClient

View file

@ -3,7 +3,6 @@ import 'package:cw_bitcoin/bitcoin_address_record.dart';
import 'package:cw_bitcoin/electrum_balance.dart'; import 'package:cw_bitcoin/electrum_balance.dart';
import 'package:cw_core/encryption_file_utils.dart'; import 'package:cw_core/encryption_file_utils.dart';
import 'package:cw_core/pathForWallet.dart'; import 'package:cw_core/pathForWallet.dart';
import 'package:cw_core/utils/file.dart';
import 'package:cw_core/wallet_type.dart'; import 'package:cw_core/wallet_type.dart';
class ElectrumWallletSnapshot { class ElectrumWallletSnapshot {
@ -15,7 +14,8 @@ class ElectrumWallletSnapshot {
required this.addresses, required this.addresses,
required this.balance, required this.balance,
required this.regularAddressIndex, required this.regularAddressIndex,
required this.changeAddressIndex}); required this.changeAddressIndex,
});
final String name; final String name;
final String password; final String password;
@ -27,8 +27,9 @@ class ElectrumWallletSnapshot {
int regularAddressIndex; int regularAddressIndex;
int changeAddressIndex; int changeAddressIndex;
static Future<ElectrumWallletSnapshot> load(EncryptionFileUtils encryptionFileUtils, String name, WalletType type, String password) async { static Future<ElectrumWallletSnapshot> load(EncryptionFileUtils encryptionFileUtils, String name,
final path = await pathForWallet(name: name, type: type); WalletType type, String password, bool isFlatpak) async {
final path = await pathForWallet(name: name, type: type, isFlatpak: isFlatpak);
final jsonSource = await encryptionFileUtils.read(path: path, password: password); final jsonSource = await encryptionFileUtils.read(path: path, password: password);
final data = json.decode(jsonSource) as Map; final data = json.decode(jsonSource) as Map;
final addressesTmp = data['addresses'] as List? ?? <Object>[]; final addressesTmp = data['addresses'] as List? ?? <Object>[];
@ -55,6 +56,7 @@ class ElectrumWallletSnapshot {
addresses: addresses, addresses: addresses,
balance: balance, balance: balance,
regularAddressIndex: regularAddressIndex, regularAddressIndex: regularAddressIndex,
changeAddressIndex: changeAddressIndex); changeAddressIndex: changeAddressIndex,
);
} }
} }

View file

@ -21,18 +21,19 @@ part 'litecoin_wallet.g.dart';
class LitecoinWallet = LitecoinWalletBase with _$LitecoinWallet; class LitecoinWallet = LitecoinWalletBase with _$LitecoinWallet;
abstract class LitecoinWalletBase extends ElectrumWallet with Store { abstract class LitecoinWalletBase extends ElectrumWallet with Store {
LitecoinWalletBase( LitecoinWalletBase({
{required String mnemonic, required String mnemonic,
required String password, required String password,
required WalletInfo walletInfo, required WalletInfo walletInfo,
required Box<UnspentCoinsInfo> unspentCoinsInfo, required Box<UnspentCoinsInfo> unspentCoinsInfo,
required Uint8List seedBytes, required Uint8List seedBytes,
required EncryptionFileUtils encryptionFileUtils, required EncryptionFileUtils encryptionFileUtils,
List<BitcoinAddressRecord>? initialAddresses, List<BitcoinAddressRecord>? initialAddresses,
ElectrumBalance? initialBalance, ElectrumBalance? initialBalance,
int initialRegularAddressIndex = 0, int initialRegularAddressIndex = 0,
int initialChangeAddressIndex = 0}) int initialChangeAddressIndex = 0,
: super( required super.isFlatpak,
}) : super(
mnemonic: mnemonic, mnemonic: mnemonic,
password: password, password: password,
walletInfo: walletInfo, walletInfo: walletInfo,
@ -44,16 +45,15 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
encryptionFileUtils: encryptionFileUtils, encryptionFileUtils: encryptionFileUtils,
currency: CryptoCurrency.ltc) { currency: CryptoCurrency.ltc) {
walletAddresses = LitecoinWalletAddresses( walletAddresses = LitecoinWalletAddresses(
walletInfo, walletInfo,
electrumClient: electrumClient, electrumClient: electrumClient,
initialAddresses: initialAddresses, initialAddresses: initialAddresses,
initialRegularAddressIndex: initialRegularAddressIndex, initialRegularAddressIndex: initialRegularAddressIndex,
initialChangeAddressIndex: initialChangeAddressIndex, initialChangeAddressIndex: initialChangeAddressIndex,
mainHd: hd, mainHd: hd,
sideHd: bitcoin.HDWallet sideHd: bitcoin.HDWallet.fromSeed(seedBytes, network: networkType).derivePath("m/0'/1"),
.fromSeed(seedBytes, network: networkType) networkType: networkType,
.derivePath("m/0'/1"), );
networkType: networkType,);
autorun((_) { autorun((_) {
this.walletAddresses.isEnabledAutoGenerateSubaddress = this.isEnabledAutoGenerateSubaddress; this.walletAddresses.isEnabledAutoGenerateSubaddress = this.isEnabledAutoGenerateSubaddress;
}); });
@ -68,19 +68,22 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
List<BitcoinAddressRecord>? initialAddresses, List<BitcoinAddressRecord>? initialAddresses,
ElectrumBalance? initialBalance, ElectrumBalance? initialBalance,
int initialRegularAddressIndex = 0, int initialRegularAddressIndex = 0,
int initialChangeAddressIndex = 0 int initialChangeAddressIndex = 0,
required bool isFlatpak,
}) async { }) async {
return LitecoinWallet( return LitecoinWallet(
mnemonic: mnemonic, mnemonic: mnemonic,
password: password, password: password,
walletInfo: walletInfo, walletInfo: walletInfo,
unspentCoinsInfo: unspentCoinsInfo, unspentCoinsInfo: unspentCoinsInfo,
initialAddresses: initialAddresses, initialAddresses: initialAddresses,
initialBalance: initialBalance, initialBalance: initialBalance,
seedBytes: await mnemonicToSeedBytes(mnemonic), seedBytes: await mnemonicToSeedBytes(mnemonic),
encryptionFileUtils: encryptionFileUtils, encryptionFileUtils: encryptionFileUtils,
initialRegularAddressIndex: initialRegularAddressIndex, initialRegularAddressIndex: initialRegularAddressIndex,
initialChangeAddressIndex: initialChangeAddressIndex); initialChangeAddressIndex: initialChangeAddressIndex,
isFlatpak: isFlatpak,
);
} }
static Future<LitecoinWallet> open({ static Future<LitecoinWallet> open({
@ -88,20 +91,24 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
required WalletInfo walletInfo, required WalletInfo walletInfo,
required Box<UnspentCoinsInfo> unspentCoinsInfo, required Box<UnspentCoinsInfo> unspentCoinsInfo,
required String password, required String password,
required EncryptionFileUtils encryptionFileUtils required EncryptionFileUtils encryptionFileUtils,
required bool isFlatpak,
}) async { }) async {
final snp = await ElectrumWallletSnapshot.load(encryptionFileUtils, name, walletInfo.type, password); final snp = await ElectrumWallletSnapshot.load(
encryptionFileUtils, name, walletInfo.type, password, isFlatpak);
return LitecoinWallet( return LitecoinWallet(
mnemonic: snp.mnemonic, mnemonic: snp.mnemonic,
password: password, password: password,
walletInfo: walletInfo, walletInfo: walletInfo,
unspentCoinsInfo: unspentCoinsInfo, unspentCoinsInfo: unspentCoinsInfo,
initialAddresses: snp.addresses, initialAddresses: snp.addresses,
initialBalance: snp.balance, initialBalance: snp.balance,
seedBytes: await mnemonicToSeedBytes(snp.mnemonic), seedBytes: await mnemonicToSeedBytes(snp.mnemonic),
encryptionFileUtils: encryptionFileUtils, encryptionFileUtils: encryptionFileUtils,
initialRegularAddressIndex: snp.regularAddressIndex, initialRegularAddressIndex: snp.regularAddressIndex,
initialChangeAddressIndex: snp.changeAddressIndex); initialChangeAddressIndex: snp.changeAddressIndex,
isFlatpak: isFlatpak,
);
} }
@override @override

View file

@ -13,15 +13,15 @@ import 'package:cw_core/wallet_info.dart';
import 'package:cw_core/wallet_base.dart'; import 'package:cw_core/wallet_base.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
class LitecoinWalletService extends WalletService< class LitecoinWalletService extends WalletService<BitcoinNewWalletCredentials,
BitcoinNewWalletCredentials, BitcoinRestoreWalletFromSeedCredentials, BitcoinRestoreWalletFromWIFCredentials> {
BitcoinRestoreWalletFromSeedCredentials, LitecoinWalletService(
BitcoinRestoreWalletFromWIFCredentials> { this.walletInfoSource, this.unspentCoinsInfoSource, this.isDirect, this.isFlatpak);
LitecoinWalletService(this.walletInfoSource, this.unspentCoinsInfoSource, this.isDirect);
final Box<WalletInfo> walletInfoSource; final Box<WalletInfo> walletInfoSource;
final Box<UnspentCoinsInfo> unspentCoinsInfoSource; final Box<UnspentCoinsInfo> unspentCoinsInfoSource;
final bool isDirect; final bool isDirect;
final bool isFlatpak;
@override @override
WalletType getType() => WalletType.litecoin; WalletType getType() => WalletType.litecoin;
@ -29,11 +29,13 @@ class LitecoinWalletService extends WalletService<
@override @override
Future<LitecoinWallet> create(BitcoinNewWalletCredentials credentials) async { Future<LitecoinWallet> create(BitcoinNewWalletCredentials credentials) async {
final wallet = await LitecoinWalletBase.create( final wallet = await LitecoinWalletBase.create(
mnemonic: await generateMnemonic(), mnemonic: await generateMnemonic(),
password: credentials.password!, password: credentials.password!,
walletInfo: credentials.walletInfo!, walletInfo: credentials.walletInfo!,
unspentCoinsInfo: unspentCoinsInfoSource, unspentCoinsInfo: unspentCoinsInfoSource,
encryptionFileUtils: encryptionFileUtilsFor(isDirect)); encryptionFileUtils: encryptionFileUtilsFor(isDirect),
isFlatpak: isFlatpak,
);
await wallet.save(); await wallet.save();
await wallet.init(); await wallet.init();
@ -42,41 +44,45 @@ class LitecoinWalletService extends WalletService<
@override @override
Future<bool> isWalletExit(String name) async => Future<bool> isWalletExit(String name) async =>
File(await pathForWallet(name: name, type: getType())).existsSync(); File(await pathForWallet(name: name, type: getType(), isFlatpak: isFlatpak)).existsSync();
@override @override
Future<LitecoinWallet> openWallet(String name, String password) async { Future<LitecoinWallet> openWallet(String name, String password) async {
final walletInfo = walletInfoSource.values.firstWhereOrNull( final walletInfo = walletInfoSource.values
(info) => info.id == WalletBase.idFor(name, getType()))!; .firstWhereOrNull((info) => info.id == WalletBase.idFor(name, getType()))!;
final wallet = await LitecoinWalletBase.open( final wallet = await LitecoinWalletBase.open(
password: password, password: password,
name: name, name: name,
walletInfo: walletInfo, walletInfo: walletInfo,
unspentCoinsInfo: unspentCoinsInfoSource, unspentCoinsInfo: unspentCoinsInfoSource,
encryptionFileUtils: encryptionFileUtilsFor(isDirect)); encryptionFileUtils: encryptionFileUtilsFor(isDirect),
isFlatpak: isFlatpak,
);
await wallet.init(); await wallet.init();
return wallet; return wallet;
} }
@override @override
Future<void> remove(String wallet) async { Future<void> remove(String wallet) async {
File(await pathForWalletDir(name: wallet, type: getType())) File(await pathForWalletDir(name: wallet, type: getType(), isFlatpak: isFlatpak))
.delete(recursive: true); .delete(recursive: true);
final walletInfo = walletInfoSource.values.firstWhereOrNull( final walletInfo = walletInfoSource.values
(info) => info.id == WalletBase.idFor(wallet, getType()))!; .firstWhereOrNull((info) => info.id == WalletBase.idFor(wallet, getType()))!;
await walletInfoSource.delete(walletInfo.key); await walletInfoSource.delete(walletInfo.key);
} }
@override @override
Future<void> rename(String currentName, String password, String newName) async { Future<void> rename(String currentName, String password, String newName) async {
final currentWalletInfo = walletInfoSource.values.firstWhereOrNull( final currentWalletInfo = walletInfoSource.values
(info) => info.id == WalletBase.idFor(currentName, getType()))!; .firstWhereOrNull((info) => info.id == WalletBase.idFor(currentName, getType()))!;
final currentWallet = await LitecoinWalletBase.open( final currentWallet = await LitecoinWalletBase.open(
password: password, password: password,
name: currentName, name: currentName,
walletInfo: currentWalletInfo, walletInfo: currentWalletInfo,
unspentCoinsInfo: unspentCoinsInfoSource, unspentCoinsInfo: unspentCoinsInfoSource,
encryptionFileUtils: encryptionFileUtilsFor(isDirect)); encryptionFileUtils: encryptionFileUtilsFor(isDirect),
isFlatpak: isFlatpak,
);
await currentWallet.renameWalletFiles(newName); await currentWallet.renameWalletFiles(newName);
@ -100,11 +106,13 @@ class LitecoinWalletService extends WalletService<
} }
final wallet = await LitecoinWalletBase.create( final wallet = await LitecoinWalletBase.create(
password: credentials.password!, password: credentials.password!,
mnemonic: credentials.mnemonic, mnemonic: credentials.mnemonic,
walletInfo: credentials.walletInfo!, walletInfo: credentials.walletInfo!,
unspentCoinsInfo: unspentCoinsInfoSource, unspentCoinsInfo: unspentCoinsInfoSource,
encryptionFileUtils: encryptionFileUtilsFor(isDirect)); encryptionFileUtils: encryptionFileUtilsFor(isDirect),
isFlatpak: isFlatpak,
);
await wallet.save(); await wallet.save();
await wallet.init(); await wallet.init();
return wallet; return wallet;

View file

@ -29,18 +29,19 @@ part 'bitcoin_cash_wallet.g.dart';
class BitcoinCashWallet = BitcoinCashWalletBase with _$BitcoinCashWallet; class BitcoinCashWallet = BitcoinCashWalletBase with _$BitcoinCashWallet;
abstract class BitcoinCashWalletBase extends ElectrumWallet with Store { abstract class BitcoinCashWalletBase extends ElectrumWallet with Store {
BitcoinCashWalletBase( BitcoinCashWalletBase({
{required String mnemonic, required String mnemonic,
required String password, required String password,
required WalletInfo walletInfo, required WalletInfo walletInfo,
required Box<UnspentCoinsInfo> unspentCoinsInfo, required Box<UnspentCoinsInfo> unspentCoinsInfo,
required Uint8List seedBytes, required Uint8List seedBytes,
required EncryptionFileUtils encryptionFileUtils, required EncryptionFileUtils encryptionFileUtils,
List<BitcoinAddressRecord>? initialAddresses, List<BitcoinAddressRecord>? initialAddresses,
ElectrumBalance? initialBalance, ElectrumBalance? initialBalance,
int initialRegularAddressIndex = 0, int initialRegularAddressIndex = 0,
int initialChangeAddressIndex = 0}) int initialChangeAddressIndex = 0,
: super( required super.isFlatpak,
}) : super(
mnemonic: mnemonic, mnemonic: mnemonic,
password: password, password: password,
walletInfo: walletInfo, walletInfo: walletInfo,
@ -57,36 +58,38 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store {
initialRegularAddressIndex: initialRegularAddressIndex, initialRegularAddressIndex: initialRegularAddressIndex,
initialChangeAddressIndex: initialChangeAddressIndex, initialChangeAddressIndex: initialChangeAddressIndex,
mainHd: hd, mainHd: hd,
sideHd: bitcoin.HDWallet.fromSeed(seedBytes) sideHd: bitcoin.HDWallet.fromSeed(seedBytes).derivePath("m/44'/145'/0'/1"),
.derivePath("m/44'/145'/0'/1"),
networkType: networkType); networkType: networkType);
autorun((_) { autorun((_) {
this.walletAddresses.isEnabledAutoGenerateSubaddress = this.isEnabledAutoGenerateSubaddress; this.walletAddresses.isEnabledAutoGenerateSubaddress = this.isEnabledAutoGenerateSubaddress;
}); });
} }
static Future<BitcoinCashWallet> create({
static Future<BitcoinCashWallet> create( required String mnemonic,
{required String mnemonic, required String password,
required String password, required WalletInfo walletInfo,
required WalletInfo walletInfo, required Box<UnspentCoinsInfo> unspentCoinsInfo,
required Box<UnspentCoinsInfo> unspentCoinsInfo, required EncryptionFileUtils encryptionFileUtils,
required EncryptionFileUtils encryptionFileUtils, List<BitcoinAddressRecord>? initialAddresses,
List<BitcoinAddressRecord>? initialAddresses, ElectrumBalance? initialBalance,
ElectrumBalance? initialBalance, int initialRegularAddressIndex = 0,
int initialRegularAddressIndex = 0, int initialChangeAddressIndex = 0,
int initialChangeAddressIndex = 0}) async { required bool isFlatpak,
}) async {
return BitcoinCashWallet( return BitcoinCashWallet(
mnemonic: mnemonic, mnemonic: mnemonic,
password: password, password: password,
walletInfo: walletInfo, walletInfo: walletInfo,
unspentCoinsInfo: unspentCoinsInfo, unspentCoinsInfo: unspentCoinsInfo,
initialAddresses: initialAddresses, initialAddresses: initialAddresses,
initialBalance: initialBalance, initialBalance: initialBalance,
seedBytes: await Mnemonic.toSeed(mnemonic), seedBytes: await Mnemonic.toSeed(mnemonic),
encryptionFileUtils: encryptionFileUtils, encryptionFileUtils: encryptionFileUtils,
initialRegularAddressIndex: initialRegularAddressIndex, initialRegularAddressIndex: initialRegularAddressIndex,
initialChangeAddressIndex: initialChangeAddressIndex); initialChangeAddressIndex: initialChangeAddressIndex,
isFlatpak: isFlatpak,
);
} }
static Future<BitcoinCashWallet> open({ static Future<BitcoinCashWallet> open({
@ -95,19 +98,23 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store {
required Box<UnspentCoinsInfo> unspentCoinsInfo, required Box<UnspentCoinsInfo> unspentCoinsInfo,
required String password, required String password,
required EncryptionFileUtils encryptionFileUtils, required EncryptionFileUtils encryptionFileUtils,
required bool isFlatpak,
}) async { }) async {
final snp = await ElectrumWallletSnapshot.load(encryptionFileUtils, name, walletInfo.type, password); final snp = await ElectrumWallletSnapshot.load(
encryptionFileUtils, name, walletInfo.type, password, isFlatpak);
return BitcoinCashWallet( return BitcoinCashWallet(
mnemonic: snp.mnemonic, mnemonic: snp.mnemonic,
password: password, password: password,
walletInfo: walletInfo, walletInfo: walletInfo,
unspentCoinsInfo: unspentCoinsInfo, unspentCoinsInfo: unspentCoinsInfo,
initialAddresses: snp.addresses, initialAddresses: snp.addresses,
initialBalance: snp.balance, initialBalance: snp.balance,
seedBytes: await Mnemonic.toSeed(snp.mnemonic), seedBytes: await Mnemonic.toSeed(snp.mnemonic),
encryptionFileUtils: encryptionFileUtils, encryptionFileUtils: encryptionFileUtils,
initialRegularAddressIndex: snp.regularAddressIndex, initialRegularAddressIndex: snp.regularAddressIndex,
initialChangeAddressIndex: snp.changeAddressIndex); initialChangeAddressIndex: snp.changeAddressIndex,
isFlatpak: isFlatpak,
);
} }
@override @override
@ -277,9 +284,7 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store {
electrumClient: electrumClient, amount: amount, fee: fee); electrumClient: electrumClient, amount: amount, fee: fee);
} }
bitbox.ECPair generateKeyPair( bitbox.ECPair generateKeyPair({required bitcoin.HDWallet hd, required int index}) =>
{required bitcoin.HDWallet hd,
required int index}) =>
bitbox.ECPair.fromWIF(hd.derive(index).wif!); bitbox.ECPair.fromWIF(hd.derive(index).wif!);
@override @override
@ -332,7 +337,8 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store {
final index = address != null final index = address != null
? walletAddresses.addresses ? walletAddresses.addresses
.firstWhere((element) => element.address == AddressUtils.toLegacyAddress(address)) .firstWhere((element) => element.address == AddressUtils.toLegacyAddress(address))
.index : null; .index
: null;
final HD = index == null ? hd : hd.derive(index); final HD = index == null ? hd : hd.derive(index);
return base64Encode(HD.signMessage(message)); return base64Encode(HD.signMessage(message));
} }

View file

@ -13,35 +13,37 @@ import 'package:collection/collection.dart';
import 'package:hive/hive.dart'; import 'package:hive/hive.dart';
class BitcoinCashWalletService extends WalletService<BitcoinCashNewWalletCredentials, class BitcoinCashWalletService extends WalletService<BitcoinCashNewWalletCredentials,
BitcoinCashRestoreWalletFromSeedCredentials, BitcoinCashRestoreWalletFromSeedCredentials, BitcoinCashRestoreWalletFromWIFCredentials> {
BitcoinCashRestoreWalletFromWIFCredentials> { BitcoinCashWalletService(
BitcoinCashWalletService(this.walletInfoSource, this.unspentCoinsInfoSource, this.isDirect); this.walletInfoSource, this.unspentCoinsInfoSource, this.isDirect, this.isFlatpak);
final Box<WalletInfo> walletInfoSource; final Box<WalletInfo> walletInfoSource;
final Box<UnspentCoinsInfo> unspentCoinsInfoSource; final Box<UnspentCoinsInfo> unspentCoinsInfoSource;
final bool isDirect; final bool isDirect;
final bool isFlatpak;
@override @override
WalletType getType() => WalletType.bitcoinCash; WalletType getType() => WalletType.bitcoinCash;
@override @override
Future<bool> isWalletExit(String name) async => Future<bool> isWalletExit(String name) async =>
File(await pathForWallet(name: name, type: getType())).existsSync(); File(await pathForWallet(name: name, type: getType(), isFlatpak: isFlatpak)).existsSync();
@override @override
Future<BitcoinCashWallet> create( Future<BitcoinCashWallet> create(credentials) async {
credentials) async {
final strength = (credentials.seedPhraseLength == 12) final strength = (credentials.seedPhraseLength == 12)
? 128 ? 128
: (credentials.seedPhraseLength == 24) : (credentials.seedPhraseLength == 24)
? 256 ? 256
: 128; : 128;
final wallet = await BitcoinCashWalletBase.create( final wallet = await BitcoinCashWalletBase.create(
mnemonic: await Mnemonic.generate(strength: strength), mnemonic: await Mnemonic.generate(strength: strength),
password: credentials.password!, password: credentials.password!,
walletInfo: credentials.walletInfo!, walletInfo: credentials.walletInfo!,
unspentCoinsInfo: unspentCoinsInfoSource, unspentCoinsInfo: unspentCoinsInfoSource,
encryptionFileUtils: encryptionFileUtilsFor(isDirect)); encryptionFileUtils: encryptionFileUtilsFor(isDirect),
isFlatpak: isFlatpak,
);
await wallet.save(); await wallet.save();
await wallet.init(); await wallet.init();
return wallet; return wallet;
@ -49,35 +51,41 @@ class BitcoinCashWalletService extends WalletService<BitcoinCashNewWalletCredent
@override @override
Future<BitcoinCashWallet> openWallet(String name, String password) async { Future<BitcoinCashWallet> openWallet(String name, String password) async {
final walletInfo = walletInfoSource.values.firstWhereOrNull( final walletInfo = walletInfoSource.values
(info) => info.id == WalletBase.idFor(name, getType()))!; .firstWhereOrNull((info) => info.id == WalletBase.idFor(name, getType()))!;
final wallet = await BitcoinCashWalletBase.open( final wallet = await BitcoinCashWalletBase.open(
password: password, name: name, walletInfo: walletInfo, password: password,
unspentCoinsInfo: unspentCoinsInfoSource, name: name,
encryptionFileUtils: encryptionFileUtilsFor(isDirect)); walletInfo: walletInfo,
unspentCoinsInfo: unspentCoinsInfoSource,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
isFlatpak: isFlatpak,
);
await wallet.init(); await wallet.init();
return wallet; return wallet;
} }
@override @override
Future<void> remove(String wallet) async { Future<void> remove(String wallet) async {
File(await pathForWalletDir(name: wallet, type: getType())) File(await pathForWalletDir(name: wallet, type: getType(), isFlatpak: isFlatpak))
.delete(recursive: true); .delete(recursive: true);
final walletInfo = walletInfoSource.values.firstWhereOrNull( final walletInfo = walletInfoSource.values
(info) => info.id == WalletBase.idFor(wallet, getType()))!; .firstWhereOrNull((info) => info.id == WalletBase.idFor(wallet, getType()))!;
await walletInfoSource.delete(walletInfo.key); await walletInfoSource.delete(walletInfo.key);
} }
@override @override
Future<void> rename(String currentName, String password, String newName) async { Future<void> rename(String currentName, String password, String newName) async {
final currentWalletInfo = walletInfoSource.values.firstWhereOrNull( final currentWalletInfo = walletInfoSource.values
(info) => info.id == WalletBase.idFor(currentName, getType()))!; .firstWhereOrNull((info) => info.id == WalletBase.idFor(currentName, getType()))!;
final currentWallet = await BitcoinCashWalletBase.open( final currentWallet = await BitcoinCashWalletBase.open(
password: password, password: password,
name: currentName, name: currentName,
walletInfo: currentWalletInfo, walletInfo: currentWalletInfo,
unspentCoinsInfo: unspentCoinsInfoSource, unspentCoinsInfo: unspentCoinsInfoSource,
encryptionFileUtils: encryptionFileUtilsFor(isDirect)); encryptionFileUtils: encryptionFileUtilsFor(isDirect),
isFlatpak: isFlatpak,
);
await currentWallet.renameWalletFiles(newName); await currentWallet.renameWalletFiles(newName);
@ -89,8 +97,7 @@ class BitcoinCashWalletService extends WalletService<BitcoinCashNewWalletCredent
} }
@override @override
Future<BitcoinCashWallet> Future<BitcoinCashWallet> restoreFromKeys(credentials) {
restoreFromKeys(credentials) {
// TODO: implement restoreFromKeys // TODO: implement restoreFromKeys
throw UnimplementedError('restoreFromKeys() is not implemented'); throw UnimplementedError('restoreFromKeys() is not implemented');
} }
@ -103,11 +110,13 @@ class BitcoinCashWalletService extends WalletService<BitcoinCashNewWalletCredent
} }
final wallet = await BitcoinCashWalletBase.create( final wallet = await BitcoinCashWalletBase.create(
password: credentials.password!, password: credentials.password!,
mnemonic: credentials.mnemonic, mnemonic: credentials.mnemonic,
walletInfo: credentials.walletInfo!, walletInfo: credentials.walletInfo!,
unspentCoinsInfo: unspentCoinsInfoSource, unspentCoinsInfo: unspentCoinsInfoSource,
encryptionFileUtils: encryptionFileUtilsFor(isDirect)); encryptionFileUtils: encryptionFileUtilsFor(isDirect),
isFlatpak: isFlatpak,
);
await wallet.save(); await wallet.save();
await wallet.init(); await wallet.init();
return wallet; return wallet;

View file

@ -10,8 +10,8 @@ String backupFileName(String originalPath) {
return pathParts.join('/'); return pathParts.join('/');
} }
Future<void> backupWalletFiles(String name) async { Future<void> backupWalletFiles(String name, bool isFlatpak) async {
final path = await pathForWallet(name: name, type: WalletType.monero); final path = await pathForWallet(name: name, type: WalletType.monero, isFlatpak: isFlatpak);
final cacheFile = File(path); final cacheFile = File(path);
final keysFile = File('$path.keys'); final keysFile = File('$path.keys');
final addressListFile = File('$path.address.txt'); final addressListFile = File('$path.address.txt');
@ -32,8 +32,9 @@ Future<void> backupWalletFiles(String name) async {
} }
} }
Future<void> restoreWalletFiles(String name) async { Future<void> restoreWalletFiles(String name, bool isFlatpak) async {
final walletDirPath = await pathForWalletDir(name: name, type: WalletType.monero); final walletDirPath =
await pathForWalletDir(name: name, type: WalletType.monero, isFlatpak: isFlatpak);
final cacheFilePath = '$walletDirPath/$name'; final cacheFilePath = '$walletDirPath/$name';
final keysFilePath = '$walletDirPath/$name.keys'; final keysFilePath = '$walletDirPath/$name.keys';
final addressListFilePath = '$walletDirPath/$name.address.txt'; final addressListFilePath = '$walletDirPath/$name.address.txt';
@ -54,8 +55,9 @@ Future<void> restoreWalletFiles(String name) async {
} }
} }
Future<bool> backupWalletFilesExists(String name) async { Future<bool> backupWalletFilesExists(String name, bool isFlatpak) async {
final walletDirPath = await pathForWalletDir(name: name, type: WalletType.monero); final walletDirPath =
await pathForWalletDir(name: name, type: WalletType.monero, isFlatpak: isFlatpak);
final cacheFilePath = '$walletDirPath/$name'; final cacheFilePath = '$walletDirPath/$name';
final keysFilePath = '$walletDirPath/$name.keys'; final keysFilePath = '$walletDirPath/$name.keys';
final addressListFilePath = '$walletDirPath/$name.address.txt'; final addressListFilePath = '$walletDirPath/$name.address.txt';
@ -63,13 +65,13 @@ Future<bool> backupWalletFilesExists(String name) async {
final backupKeysFile = File(backupFileName(keysFilePath)); final backupKeysFile = File(backupFileName(keysFilePath));
final backupAddressListFile = File(backupFileName(addressListFilePath)); final backupAddressListFile = File(backupFileName(addressListFilePath));
return backupCacheFile.existsSync() return backupCacheFile.existsSync() &&
&& backupKeysFile.existsSync() backupKeysFile.existsSync() &&
&& backupAddressListFile.existsSync(); backupAddressListFile.existsSync();
} }
Future<void> removeCache(String name) async { Future<void> removeCache(String name, bool isFlatpak) async {
final path = await pathForWallet(name: name, type: WalletType.monero); final path = await pathForWallet(name: name, type: WalletType.monero, isFlatpak: isFlatpak);
final cacheFile = File(path); final cacheFile = File(path);
if (cacheFile.existsSync()) { if (cacheFile.existsSync()) {
@ -77,12 +79,12 @@ Future<void> removeCache(String name) async {
} }
} }
Future<void> restoreOrResetWalletFiles(String name) async { Future<void> restoreOrResetWalletFiles(String name, bool isFlatpak) async {
final backupsExists = await backupWalletFilesExists(name); final backupsExists = await backupWalletFilesExists(name, isFlatpak);
if (backupsExists) { if (backupsExists) {
await restoreWalletFiles(name); await restoreWalletFiles(name, isFlatpak);
} }
removeCache(name); removeCache(name, isFlatpak);
} }

View file

@ -1,11 +1,10 @@
import 'dart:io'; import 'dart:io';
import 'package:cw_core/root_dir.dart'; import 'package:cw_core/root_dir.dart';
import 'package:cw_core/wallet_type.dart'; import 'package:cw_core/wallet_type.dart';
import 'package:flutter/foundation.dart';
import 'package:path_provider/path_provider.dart';
Future<String> pathForWalletDir({required String name, required WalletType type}) async { Future<String> pathForWalletDir(
final root = await getAppDir(); {required String name, required WalletType type, required bool isFlatpak}) async {
final root = await getAppDir(isFlatpak: isFlatpak);
final prefix = walletTypeToString(type).toLowerCase(); final prefix = walletTypeToString(type).toLowerCase();
final walletsDir = Directory('${root.path}/wallets'); final walletsDir = Directory('${root.path}/wallets');
final walletDire = Directory('${walletsDir.path}/$prefix/$name'); final walletDire = Directory('${walletsDir.path}/$prefix/$name');
@ -17,12 +16,13 @@ Future<String> pathForWalletDir({required String name, required WalletType type
return walletDire.path; return walletDire.path;
} }
Future<String> pathForWallet({required String name, required WalletType type}) async => Future<String> pathForWallet(
await pathForWalletDir(name: name, type: type) {required String name, required WalletType type, required bool isFlatpak}) async =>
await pathForWalletDir(name: name, type: type, isFlatpak: isFlatpak)
.then((path) => path + '/$name'); .then((path) => path + '/$name');
Future<String> outdatedAndroidPathForWalletDir({required String name}) async { Future<String> outdatedAndroidPathForWalletDir({required String name, required bool isFlatpak}) async {
final directory = await getAppDir(); final directory = await getAppDir(isFlatpak: isFlatpak);
final pathDir = directory.path + '/$name'; final pathDir = directory.path + '/$name';
return pathDir; return pathDir;

View file

@ -5,7 +5,7 @@ String? _rootDirPath;
void setRootDirFromEnv() => _rootDirPath = Platform.environment['CAKE_WALLET_DIR']; void setRootDirFromEnv() => _rootDirPath = Platform.environment['CAKE_WALLET_DIR'];
Future<Directory> getAppDir({String appName = 'cake_wallet'}) async { Future<Directory> getAppDir({String appName = 'cake_wallet', required bool isFlatpak}) async {
Directory dir; Directory dir;
if (_rootDirPath != null && _rootDirPath!.isNotEmpty) { if (_rootDirPath != null && _rootDirPath!.isNotEmpty) {
@ -13,13 +13,23 @@ Future<Directory> getAppDir({String appName = 'cake_wallet'}) async {
dir.create(recursive: true); dir.create(recursive: true);
} else { } else {
if (Platform.isLinux) { if (Platform.isLinux) {
String appDirPath; String appDirPath = '';
try { if (isFlatpak) {
dir = await getApplicationDocumentsDirectory(); appDirPath =
appDirPath = '${dir.path}/$appName'; '/home/${Platform.environment['USER']}/.var/app/com.cakewallet.CakeWallet/data/.$appName';
} catch (e) { } else {
appDirPath = '/home/${Platform.environment['USER']}/.$appName'; final homePath = '/home/${Platform.environment['USER']}/.$appName';
if (await Directory(homePath).exists()) {
appDirPath = homePath;
} else {
final docPath = await getApplicationDocumentsDirectory();
if (await docPath.exists()) {
appDirPath = docPath.path;
}
}
} }
dir = Directory.fromUri(Uri.file(appDirPath)); dir = Directory.fromUri(Uri.file(appDirPath));

View file

@ -19,12 +19,14 @@ abstract class EthereumTransactionHistoryBase
required this.walletInfo, required this.walletInfo,
required String password, required String password,
required this.encryptionFileUtils, required this.encryptionFileUtils,
required this.isFlatpak,
}) : _password = password { }) : _password = password {
transactions = ObservableMap<String, EthereumTransactionInfo>(); transactions = ObservableMap<String, EthereumTransactionInfo>();
} }
final WalletInfo walletInfo; final WalletInfo walletInfo;
final EncryptionFileUtils encryptionFileUtils; final EncryptionFileUtils encryptionFileUtils;
final bool isFlatpak;
String _password; String _password;
Future<void> init() async => await _load(); Future<void> init() async => await _load();
@ -32,7 +34,8 @@ abstract class EthereumTransactionHistoryBase
@override @override
Future<void> save() async { Future<void> save() async {
try { try {
final dirPath = await pathForWalletDir(name: walletInfo.name, type: walletInfo.type); final dirPath = await pathForWalletDir(
name: walletInfo.name, type: walletInfo.type, isFlatpak: isFlatpak);
final path = '$dirPath/$transactionsHistoryFileName'; final path = '$dirPath/$transactionsHistoryFileName';
final data = json.encode({'transactions': transactions}); final data = json.encode({'transactions': transactions});
await encryptionFileUtils.write(path: path, password: _password, data: data); await encryptionFileUtils.write(path: path, password: _password, data: data);
@ -50,7 +53,8 @@ abstract class EthereumTransactionHistoryBase
this.transactions.addAll(transactions); this.transactions.addAll(transactions);
Future<Map<String, dynamic>> _read() async { Future<Map<String, dynamic>> _read() async {
final dirPath = await pathForWalletDir(name: walletInfo.name, type: walletInfo.type); final dirPath =
await pathForWalletDir(name: walletInfo.name, type: walletInfo.type, isFlatpak: isFlatpak);
final path = '$dirPath/$transactionsHistoryFileName'; final path = '$dirPath/$transactionsHistoryFileName';
final content = await encryptionFileUtils.read(path: path, password: _password); final content = await encryptionFileUtils.read(path: path, password: _password);
if (content.isEmpty) { if (content.isEmpty) {

View file

@ -50,6 +50,7 @@ abstract class EthereumWalletBase
required String password, required String password,
required EncryptionFileUtils encryptionFileUtils, required EncryptionFileUtils encryptionFileUtils,
ERC20Balance? initialBalance, ERC20Balance? initialBalance,
required this.isFlatpak,
}) : syncStatus = NotConnectedSyncStatus(), }) : syncStatus = NotConnectedSyncStatus(),
_password = password, _password = password,
_mnemonic = mnemonic, _mnemonic = mnemonic,
@ -75,6 +76,8 @@ abstract class EthereumWalletBase
_sharedPrefs.complete(SharedPreferences.getInstance()); _sharedPrefs.complete(SharedPreferences.getInstance());
} }
final bool isFlatpak;
final String? _mnemonic; final String? _mnemonic;
final String? _hexPrivateKey; final String? _hexPrivateKey;
final String _password; final String _password;
@ -374,7 +377,8 @@ abstract class EthereumWalletBase
} }
} }
Future<String> makePath() async => pathForWallet(name: walletInfo.name, type: walletInfo.type); Future<String> makePath() async =>
pathForWallet(name: walletInfo.name, type: walletInfo.type, isFlatpak: isFlatpak);
String toJSON() => json.encode({ String toJSON() => json.encode({
'mnemonic': _mnemonic, 'mnemonic': _mnemonic,
@ -387,8 +391,9 @@ abstract class EthereumWalletBase
required String password, required String password,
required WalletInfo walletInfo, required WalletInfo walletInfo,
required EncryptionFileUtils encryptionFileUtils, required EncryptionFileUtils encryptionFileUtils,
required bool isFlatpak,
}) async { }) async {
final path = await pathForWallet(name: name, type: walletInfo.type); final path = await pathForWallet(name: name, type: walletInfo.type, isFlatpak: isFlatpak);
final jsonSource = await encryptionFileUtils.read(path: path, password: password); final jsonSource = await encryptionFileUtils.read(path: path, password: password);
final data = json.decode(jsonSource) as Map; final data = json.decode(jsonSource) as Map;
final mnemonic = data['mnemonic'] as String?; final mnemonic = data['mnemonic'] as String?;
@ -508,19 +513,23 @@ abstract class EthereumWalletBase
@override @override
Future<void> renameWalletFiles(String newWalletName) async { Future<void> renameWalletFiles(String newWalletName) async {
final currentWalletPath = await pathForWallet(name: walletInfo.name, type: type); final currentWalletPath =
await pathForWallet(name: walletInfo.name, type: type, isFlatpak: isFlatpak);
final currentWalletFile = File(currentWalletPath); final currentWalletFile = File(currentWalletPath);
final currentDirPath = await pathForWalletDir(name: walletInfo.name, type: type); final currentDirPath =
await pathForWalletDir(name: walletInfo.name, type: type, isFlatpak: isFlatpak);
final currentTransactionsFile = File('$currentDirPath/$transactionsHistoryFileName'); final currentTransactionsFile = File('$currentDirPath/$transactionsHistoryFileName');
// Copies current wallet files into new wallet name's dir and files // Copies current wallet files into new wallet name's dir and files
if (currentWalletFile.existsSync()) { if (currentWalletFile.existsSync()) {
final newWalletPath = await pathForWallet(name: newWalletName, type: type); final newWalletPath =
await pathForWallet(name: newWalletName, type: type, isFlatpak: isFlatpak);
await currentWalletFile.copy(newWalletPath); await currentWalletFile.copy(newWalletPath);
} }
if (currentTransactionsFile.existsSync()) { if (currentTransactionsFile.existsSync()) {
final newDirPath = await pathForWalletDir(name: newWalletName, type: type); final newDirPath =
await pathForWalletDir(name: newWalletName, type: type, isFlatpak: isFlatpak);
await currentTransactionsFile.copy('$newDirPath/$transactionsHistoryFileName'); await currentTransactionsFile.copy('$newDirPath/$transactionsHistoryFileName');
} }

View file

@ -15,10 +15,11 @@ import 'package:collection/collection.dart';
class EthereumWalletService extends WalletService<EthereumNewWalletCredentials, class EthereumWalletService extends WalletService<EthereumNewWalletCredentials,
EthereumRestoreWalletFromSeedCredentials, EthereumRestoreWalletFromPrivateKey> { EthereumRestoreWalletFromSeedCredentials, EthereumRestoreWalletFromPrivateKey> {
EthereumWalletService(this.walletInfoSource, this.isDirect); EthereumWalletService(this.walletInfoSource, this.isDirect, this.isFlatpak);
final Box<WalletInfo> walletInfoSource; final Box<WalletInfo> walletInfoSource;
final bool isDirect; final bool isDirect;
final bool isFlatpak;
@override @override
Future<EthereumWallet> create(EthereumNewWalletCredentials credentials) async { Future<EthereumWallet> create(EthereumNewWalletCredentials credentials) async {
@ -30,6 +31,7 @@ class EthereumWalletService extends WalletService<EthereumNewWalletCredentials,
mnemonic: mnemonic, mnemonic: mnemonic,
password: credentials.password!, password: credentials.password!,
encryptionFileUtils: encryptionFileUtilsFor(isDirect), encryptionFileUtils: encryptionFileUtilsFor(isDirect),
isFlatpak: isFlatpak,
); );
await wallet.init(); await wallet.init();
@ -44,7 +46,7 @@ class EthereumWalletService extends WalletService<EthereumNewWalletCredentials,
@override @override
Future<bool> isWalletExit(String name) async => Future<bool> isWalletExit(String name) async =>
File(await pathForWallet(name: name, type: getType())).existsSync(); File(await pathForWallet(name: name, type: getType(), isFlatpak: isFlatpak)).existsSync();
@override @override
Future<EthereumWallet> openWallet(String name, String password) async { Future<EthereumWallet> openWallet(String name, String password) async {
@ -55,6 +57,7 @@ class EthereumWalletService extends WalletService<EthereumNewWalletCredentials,
password: password, password: password,
walletInfo: walletInfo, walletInfo: walletInfo,
encryptionFileUtils: encryptionFileUtilsFor(isDirect), encryptionFileUtils: encryptionFileUtilsFor(isDirect),
isFlatpak: isFlatpak,
); );
await wallet.init(); await wallet.init();
@ -65,7 +68,8 @@ class EthereumWalletService extends WalletService<EthereumNewWalletCredentials,
@override @override
Future<void> remove(String wallet) async { Future<void> remove(String wallet) async {
File(await pathForWalletDir(name: wallet, type: getType())).delete(recursive: true); File(await pathForWalletDir(name: wallet, type: getType(), isFlatpak: isFlatpak))
.delete(recursive: true);
final walletInfo = walletInfoSource.values final walletInfo = walletInfoSource.values
.firstWhereOrNull((info) => info.id == WalletBase.idFor(wallet, getType()))!; .firstWhereOrNull((info) => info.id == WalletBase.idFor(wallet, getType()))!;
await walletInfoSource.delete(walletInfo.key); await walletInfoSource.delete(walletInfo.key);
@ -78,6 +82,7 @@ class EthereumWalletService extends WalletService<EthereumNewWalletCredentials,
privateKey: credentials.privateKey, privateKey: credentials.privateKey,
walletInfo: credentials.walletInfo!, walletInfo: credentials.walletInfo!,
encryptionFileUtils: encryptionFileUtilsFor(isDirect), encryptionFileUtils: encryptionFileUtilsFor(isDirect),
isFlatpak: isFlatpak,
); );
await wallet.init(); await wallet.init();
@ -99,6 +104,7 @@ class EthereumWalletService extends WalletService<EthereumNewWalletCredentials,
mnemonic: credentials.mnemonic, mnemonic: credentials.mnemonic,
walletInfo: credentials.walletInfo!, walletInfo: credentials.walletInfo!,
encryptionFileUtils: encryptionFileUtilsFor(isDirect), encryptionFileUtils: encryptionFileUtilsFor(isDirect),
isFlatpak: isFlatpak,
); );
await wallet.init(); await wallet.init();
@ -117,6 +123,7 @@ class EthereumWalletService extends WalletService<EthereumNewWalletCredentials,
name: currentName, name: currentName,
walletInfo: currentWalletInfo, walletInfo: currentWalletInfo,
encryptionFileUtils: encryptionFileUtilsFor(isDirect), encryptionFileUtils: encryptionFileUtilsFor(isDirect),
isFlatpak: isFlatpak,
); );
await currentWallet.renameWalletFiles(newName); await currentWallet.renameWalletFiles(newName);

View file

@ -41,12 +41,14 @@ const moneroBlockSize = 1000;
class MoneroWallet = MoneroWalletBase with _$MoneroWallet; class MoneroWallet = MoneroWalletBase with _$MoneroWallet;
abstract class MoneroWalletBase extends WalletBase<MoneroBalance, abstract class MoneroWalletBase
MoneroTransactionHistory, MoneroTransactionInfo> with Store { extends WalletBase<MoneroBalance, MoneroTransactionHistory, MoneroTransactionInfo> with Store {
MoneroWalletBase({required WalletInfo walletInfo, MoneroWalletBase({
required WalletInfo walletInfo,
required Box<UnspentCoinsInfo> unspentCoinsInfo, required Box<UnspentCoinsInfo> unspentCoinsInfo,
required String password}) required String password,
: balance = ObservableMap<CryptoCurrency, MoneroBalance>.of({ required this.isFlatpak,
}) : balance = ObservableMap<CryptoCurrency, MoneroBalance>.of({
CryptoCurrency.xmr: MoneroBalance( CryptoCurrency.xmr: MoneroBalance(
fullBalance: monero_wallet.getFullBalance(accountIndex: 0), fullBalance: monero_wallet.getFullBalance(accountIndex: 0),
unlockedBalance: monero_wallet.getFullBalance(accountIndex: 0)) unlockedBalance: monero_wallet.getFullBalance(accountIndex: 0))
@ -79,6 +81,8 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
}); });
} }
final bool isFlatpak;
static const int _autoSaveInterval = 30; static const int _autoSaveInterval = 30;
Box<UnspentCoinsInfo> unspentCoinsInfo; Box<UnspentCoinsInfo> unspentCoinsInfo;
@ -315,18 +319,21 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
} }
await walletAddresses.updateAddressesInBox(); await walletAddresses.updateAddressesInBox();
await backupWalletFiles(name); await backupWalletFiles(name, isFlatpak);
await monero_wallet.store(); await monero_wallet.store();
} }
@override @override
Future<void> renameWalletFiles(String newWalletName) async { Future<void> renameWalletFiles(String newWalletName) async {
final currentWalletDirPath = await pathForWalletDir(name: name, type: type); final currentWalletDirPath =
await pathForWalletDir(name: name, type: type, isFlatpak: isFlatpak);
try { try {
// -- rename the waller folder -- // -- rename the waller folder --
final currentWalletDir = Directory(await pathForWalletDir(name: name, type: type)); final currentWalletDir =
final newWalletDirPath = await pathForWalletDir(name: newWalletName, type: type); Directory(await pathForWalletDir(name: name, type: type, isFlatpak: isFlatpak));
final newWalletDirPath =
await pathForWalletDir(name: newWalletName, type: type, isFlatpak: isFlatpak);
await currentWalletDir.rename(newWalletDirPath); await currentWalletDir.rename(newWalletDirPath);
// -- use new waller folder to rename files with old names still -- // -- use new waller folder to rename files with old names still --
@ -336,7 +343,8 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
final currentKeysFile = File('$renamedWalletPath.keys'); final currentKeysFile = File('$renamedWalletPath.keys');
final currentAddressListFile = File('$renamedWalletPath.address.txt'); final currentAddressListFile = File('$renamedWalletPath.address.txt');
final newWalletPath = await pathForWallet(name: newWalletName, type: type); final newWalletPath =
await pathForWallet(name: newWalletName, type: type, isFlatpak: isFlatpak);
if (currentCacheFile.existsSync()) { if (currentCacheFile.existsSync()) {
await currentCacheFile.rename(newWalletPath); await currentCacheFile.rename(newWalletPath);
@ -348,13 +356,14 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
await currentAddressListFile.rename('$newWalletPath.address.txt'); await currentAddressListFile.rename('$newWalletPath.address.txt');
} }
} catch (e) { } catch (e) {
final currentWalletPath = await pathForWallet(name: name, type: type); final currentWalletPath = await pathForWallet(name: name, type: type, isFlatpak: isFlatpak);
final currentCacheFile = File(currentWalletPath); final currentCacheFile = File(currentWalletPath);
final currentKeysFile = File('$currentWalletPath.keys'); final currentKeysFile = File('$currentWalletPath.keys');
final currentAddressListFile = File('$currentWalletPath.address.txt'); final currentAddressListFile = File('$currentWalletPath.address.txt');
final newWalletPath = await pathForWallet(name: newWalletName, type: type); final newWalletPath =
await pathForWallet(name: newWalletName, type: type, isFlatpak: isFlatpak);
// Copies current wallet files into new wallet name's dir and files // Copies current wallet files into new wallet name's dir and files
if (currentCacheFile.existsSync()) { if (currentCacheFile.existsSync()) {
@ -413,9 +422,7 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
if (coin.spent == 0) { if (coin.spent == 0) {
final unspent = MoneroUnspent.fromCoinsInfoRow(coin); final unspent = MoneroUnspent.fromCoinsInfoRow(coin);
if (unspent.hash.isNotEmpty) { if (unspent.hash.isNotEmpty) {
unspent.isChange = transaction_history unspent.isChange = transaction_history.getTransaction(unspent.hash).direction == 1;
.getTransaction(unspent.hash)
.direction == 1;
} }
unspentCoins.add(unspent); unspentCoins.add(unspent);
} }
@ -429,7 +436,7 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
if (unspentCoins.isNotEmpty) { if (unspentCoins.isNotEmpty) {
unspentCoins.forEach((coin) { unspentCoins.forEach((coin) {
final coinInfoList = unspentCoinsInfo.values.where((element) => final coinInfoList = unspentCoinsInfo.values.where((element) =>
element.walletId.contains(id) && element.walletId.contains(id) &&
element.accountIndex == walletAddresses.account!.id && element.accountIndex == walletAddresses.account!.id &&
element.keyImage!.contains(coin.keyImage!)); element.keyImage!.contains(coin.keyImage!));

View file

@ -15,7 +15,8 @@ import 'package:hive/hive.dart';
import 'package:polyseed/polyseed.dart'; import 'package:polyseed/polyseed.dart';
class MoneroNewWalletCredentials extends WalletCredentials { class MoneroNewWalletCredentials extends WalletCredentials {
MoneroNewWalletCredentials({required String name, required this.language, required this.isPolyseed, String? password}) MoneroNewWalletCredentials(
{required String name, required this.language, required this.isPolyseed, String? password})
: super(name: name, password: password); : super(name: name, password: password);
final String language; final String language;
@ -52,14 +53,13 @@ class MoneroRestoreWalletFromKeysCredentials extends WalletCredentials {
final String spendKey; final String spendKey;
} }
class MoneroWalletService extends WalletService< class MoneroWalletService extends WalletService<MoneroNewWalletCredentials,
MoneroNewWalletCredentials, MoneroRestoreWalletFromSeedCredentials, MoneroRestoreWalletFromKeysCredentials> {
MoneroRestoreWalletFromSeedCredentials, MoneroWalletService(this.walletInfoSource, this.unspentCoinsInfoSource, this.isFlatpak);
MoneroRestoreWalletFromKeysCredentials> {
MoneroWalletService(this.walletInfoSource, this.unspentCoinsInfoSource);
final Box<WalletInfo> walletInfoSource; final Box<WalletInfo> walletInfoSource;
final Box<UnspentCoinsInfo> unspentCoinsInfoSource; final Box<UnspentCoinsInfo> unspentCoinsInfoSource;
final bool isFlatpak;
static bool walletFilesExist(String path) => static bool walletFilesExist(String path) =>
!File(path).existsSync() && !File('$path.keys').existsSync(); !File(path).existsSync() && !File('$path.keys').existsSync();
@ -70,7 +70,8 @@ class MoneroWalletService extends WalletService<
@override @override
Future<MoneroWallet> create(MoneroNewWalletCredentials credentials) async { Future<MoneroWallet> create(MoneroNewWalletCredentials credentials) async {
try { try {
final path = await pathForWallet(name: credentials.name, type: getType()); final path =
await pathForWallet(name: credentials.name, type: getType(), isFlatpak: isFlatpak);
if (credentials.isPolyseed) { if (credentials.isPolyseed) {
final polyseed = Polyseed.create(); final polyseed = Polyseed.create();
@ -89,7 +90,9 @@ class MoneroWalletService extends WalletService<
final wallet = MoneroWallet( final wallet = MoneroWallet(
walletInfo: credentials.walletInfo!, walletInfo: credentials.walletInfo!,
unspentCoinsInfo: unspentCoinsInfoSource, unspentCoinsInfo: unspentCoinsInfoSource,
password: credentials.password!); password: credentials.password!,
isFlatpak: isFlatpak,
);
await wallet.init(); await wallet.init();
return wallet; return wallet;
@ -103,7 +106,7 @@ class MoneroWalletService extends WalletService<
@override @override
Future<bool> isWalletExit(String name) async { Future<bool> isWalletExit(String name) async {
try { try {
final path = await pathForWallet(name: name, type: getType()); final path = await pathForWallet(name: name, type: getType(), isFlatpak: isFlatpak);
return monero_wallet_manager.isWalletExist(path: path); return monero_wallet_manager.isWalletExist(path: path);
} catch (e) { } catch (e) {
// TODO: Implement Exception for wallet list service. // TODO: Implement Exception for wallet list service.
@ -115,24 +118,24 @@ class MoneroWalletService extends WalletService<
@override @override
Future<MoneroWallet> openWallet(String name, String password) async { Future<MoneroWallet> openWallet(String name, String password) async {
try { try {
final path = await pathForWallet(name: name, type: getType()); final path = await pathForWallet(name: name, type: getType(), isFlatpak: isFlatpak);
if (walletFilesExist(path)) { if (walletFilesExist(path)) {
await repairOldAndroidWallet(name); await repairOldAndroidWallet(name);
} }
await monero_wallet_manager await monero_wallet_manager.openWalletAsync({'path': path, 'password': password});
.openWalletAsync({'path': path, 'password': password}); final walletInfo = walletInfoSource.values
final walletInfo = walletInfoSource.values.firstWhere( .firstWhere((info) => info.id == WalletBase.idFor(name, getType()));
(info) => info.id == WalletBase.idFor(name, getType()));
final wallet = MoneroWallet( final wallet = MoneroWallet(
walletInfo: walletInfo, walletInfo: walletInfo,
unspentCoinsInfo: unspentCoinsInfoSource, unspentCoinsInfo: unspentCoinsInfoSource,
password: password); password: password,
isFlatpak: isFlatpak);
final isValid = wallet.walletAddresses.validate(); final isValid = wallet.walletAddresses.validate();
if (!isValid) { if (!isValid) {
await restoreOrResetWalletFiles(name); await restoreOrResetWalletFiles(name, isFlatpak);
wallet.close(); wallet.close();
return openWallet(name, password); return openWallet(name, password);
} }
@ -166,7 +169,7 @@ class MoneroWalletService extends WalletService<
isMissingCacheFilesIOS || isMissingCacheFilesIOS ||
isMissingCacheFilesAndroid || isMissingCacheFilesAndroid ||
invalidSignature) { invalidSignature) {
await restoreOrResetWalletFiles(name); await restoreOrResetWalletFiles(name, isFlatpak);
return openWallet(name, password); return openWallet(name, password);
} }
@ -176,7 +179,7 @@ class MoneroWalletService extends WalletService<
@override @override
Future<void> remove(String wallet) async { Future<void> remove(String wallet) async {
final path = await pathForWalletDir(name: wallet, type: getType()); final path = await pathForWalletDir(name: wallet, type: getType(), isFlatpak: isFlatpak);
final file = Directory(path); final file = Directory(path);
final isExist = file.existsSync(); final isExist = file.existsSync();
@ -190,14 +193,14 @@ class MoneroWalletService extends WalletService<
} }
@override @override
Future<void> rename( Future<void> rename(String currentName, String password, String newName) async {
String currentName, String password, String newName) async { final currentWalletInfo = walletInfoSource.values
final currentWalletInfo = walletInfoSource.values.firstWhere( .firstWhere((info) => info.id == WalletBase.idFor(currentName, getType()));
(info) => info.id == WalletBase.idFor(currentName, getType()));
final currentWallet = MoneroWallet( final currentWallet = MoneroWallet(
walletInfo: currentWalletInfo, walletInfo: currentWalletInfo,
unspentCoinsInfo: unspentCoinsInfoSource, unspentCoinsInfo: unspentCoinsInfoSource,
password: password, password: password,
isFlatpak: isFlatpak,
); );
await currentWallet.renameWalletFiles(newName); await currentWallet.renameWalletFiles(newName);
@ -210,10 +213,10 @@ class MoneroWalletService extends WalletService<
} }
@override @override
Future<MoneroWallet> restoreFromKeys( Future<MoneroWallet> restoreFromKeys(MoneroRestoreWalletFromKeysCredentials credentials) async {
MoneroRestoreWalletFromKeysCredentials credentials) async {
try { try {
final path = await pathForWallet(name: credentials.name, type: getType()); final path =
await pathForWallet(name: credentials.name, type: getType(), isFlatpak: isFlatpak);
await monero_wallet_manager.restoreFromKeys( await monero_wallet_manager.restoreFromKeys(
path: path, path: path,
password: credentials.password!, password: credentials.password!,
@ -225,7 +228,9 @@ class MoneroWalletService extends WalletService<
final wallet = MoneroWallet( final wallet = MoneroWallet(
walletInfo: credentials.walletInfo!, walletInfo: credentials.walletInfo!,
unspentCoinsInfo: unspentCoinsInfoSource, unspentCoinsInfo: unspentCoinsInfoSource,
password: credentials.password!); password: credentials.password!,
isFlatpak: isFlatpak,
);
await wallet.init(); await wallet.init();
return wallet; return wallet;
@ -237,16 +242,15 @@ class MoneroWalletService extends WalletService<
} }
@override @override
Future<MoneroWallet> restoreFromSeed( Future<MoneroWallet> restoreFromSeed(MoneroRestoreWalletFromSeedCredentials credentials) async {
MoneroRestoreWalletFromSeedCredentials credentials) async {
// Restore from Polyseed // Restore from Polyseed
if (Polyseed.isValidSeed(credentials.mnemonic)) { if (Polyseed.isValidSeed(credentials.mnemonic)) {
return restoreFromPolyseed(credentials); return restoreFromPolyseed(credentials);
} }
try { try {
final path = await pathForWallet(name: credentials.name, type: getType()); final path =
await pathForWallet(name: credentials.name, type: getType(), isFlatpak: isFlatpak);
await monero_wallet_manager.restoreFromSeed( await monero_wallet_manager.restoreFromSeed(
path: path, path: path,
password: credentials.password!, password: credentials.password!,
@ -255,7 +259,9 @@ class MoneroWalletService extends WalletService<
final wallet = MoneroWallet( final wallet = MoneroWallet(
walletInfo: credentials.walletInfo!, walletInfo: credentials.walletInfo!,
unspentCoinsInfo: unspentCoinsInfoSource, unspentCoinsInfo: unspentCoinsInfoSource,
password: credentials.password!); password: credentials.password!,
isFlatpak: isFlatpak,
);
await wallet.init(); await wallet.init();
return wallet; return wallet;
@ -266,14 +272,17 @@ class MoneroWalletService extends WalletService<
} }
} }
Future<MoneroWallet> restoreFromPolyseed(MoneroRestoreWalletFromSeedCredentials credentials) async { Future<MoneroWallet> restoreFromPolyseed(
MoneroRestoreWalletFromSeedCredentials credentials) async {
try { try {
final path = await pathForWallet(name: credentials.name, type: getType()); final path =
await pathForWallet(name: credentials.name, type: getType(), isFlatpak: isFlatpak);
final polyseedCoin = PolyseedCoin.POLYSEED_MONERO; final polyseedCoin = PolyseedCoin.POLYSEED_MONERO;
final lang = PolyseedLang.getByPhrase(credentials.mnemonic); final lang = PolyseedLang.getByPhrase(credentials.mnemonic);
final polyseed = Polyseed.decode(credentials.mnemonic, lang, polyseedCoin); final polyseed = Polyseed.decode(credentials.mnemonic, lang, polyseedCoin);
return _restoreFromPolyseed(path, credentials.password!, polyseed, credentials.walletInfo!, lang); return _restoreFromPolyseed(
path, credentials.password!, polyseed, credentials.walletInfo!, lang);
} catch (e) { } catch (e) {
// TODO: Implement Exception for wallet list service. // TODO: Implement Exception for wallet list service.
print('MoneroWalletsManager Error: $e'); print('MoneroWalletsManager Error: $e');
@ -281,11 +290,11 @@ class MoneroWalletService extends WalletService<
} }
} }
Future<MoneroWallet> _restoreFromPolyseed(String path, String password, Polyseed polyseed, Future<MoneroWallet> _restoreFromPolyseed(
WalletInfo walletInfo, PolyseedLang lang, String path, String password, Polyseed polyseed, WalletInfo walletInfo, PolyseedLang lang,
{PolyseedCoin coin = PolyseedCoin.POLYSEED_MONERO, int? overrideHeight}) async { {PolyseedCoin coin = PolyseedCoin.POLYSEED_MONERO, int? overrideHeight}) async {
final height = overrideHeight ?? getMoneroHeigthByDate( final height = overrideHeight ??
date: DateTime.fromMillisecondsSinceEpoch(polyseed.birthday * 1000)); getMoneroHeigthByDate(date: DateTime.fromMillisecondsSinceEpoch(polyseed.birthday * 1000));
final spendKey = polyseed.generateKey(coin, 32).toHexString(); final spendKey = polyseed.generateKey(coin, 32).toHexString();
final seed = polyseed.encode(lang, coin); final seed = polyseed.encode(lang, coin);
@ -304,6 +313,7 @@ class MoneroWalletService extends WalletService<
walletInfo: walletInfo, walletInfo: walletInfo,
unspentCoinsInfo: unspentCoinsInfoSource, unspentCoinsInfo: unspentCoinsInfoSource,
password: password, password: password,
isFlatpak: isFlatpak,
); );
await wallet.init(); await wallet.init();
@ -317,7 +327,7 @@ class MoneroWalletService extends WalletService<
} }
final oldAndroidWalletDirPath = final oldAndroidWalletDirPath =
await outdatedAndroidPathForWalletDir(name: name); await outdatedAndroidPathForWalletDir(name: name, isFlatpak: isFlatpak);
final dir = Directory(oldAndroidWalletDirPath); final dir = Directory(oldAndroidWalletDirPath);
if (!dir.existsSync()) { if (!dir.existsSync()) {
@ -325,7 +335,7 @@ class MoneroWalletService extends WalletService<
} }
final newWalletDirPath = final newWalletDirPath =
await pathForWalletDir(name: name, type: getType()); await pathForWalletDir(name: name, type: getType(), isFlatpak: isFlatpak);
dir.listSync().forEach((f) { dir.listSync().forEach((f) {
final file = File(f.path); final file = File(f.path);

View file

@ -19,6 +19,7 @@ abstract class NanoTransactionHistoryBase extends TransactionHistoryBase<NanoTra
required this.walletInfo, required this.walletInfo,
required String password, required String password,
required this.encryptionFileUtils, required this.encryptionFileUtils,
required this.isFlatpak,
}) : _password = password { }) : _password = password {
transactions = ObservableMap<String, NanoTransactionInfo>(); transactions = ObservableMap<String, NanoTransactionInfo>();
} }
@ -26,13 +27,15 @@ abstract class NanoTransactionHistoryBase extends TransactionHistoryBase<NanoTra
final WalletInfo walletInfo; final WalletInfo walletInfo;
final EncryptionFileUtils encryptionFileUtils; final EncryptionFileUtils encryptionFileUtils;
String _password; String _password;
final bool isFlatpak;
Future<void> init() async => await _load(); Future<void> init() async => await _load();
@override @override
Future<void> save() async { Future<void> save() async {
try { try {
final dirPath = await pathForWalletDir(name: walletInfo.name, type: walletInfo.type); final dirPath = await pathForWalletDir(
name: walletInfo.name, type: walletInfo.type, isFlatpak: isFlatpak);
final path = '$dirPath/$transactionsHistoryFileName'; final path = '$dirPath/$transactionsHistoryFileName';
final data = json.encode({'transactions': transactions}); final data = json.encode({'transactions': transactions});
await encryptionFileUtils.write(path: path, password: _password, data: data); await encryptionFileUtils.write(path: path, password: _password, data: data);
@ -49,7 +52,8 @@ abstract class NanoTransactionHistoryBase extends TransactionHistoryBase<NanoTra
this.transactions.addAll(transactions); this.transactions.addAll(transactions);
Future<Map<String, dynamic>> _read() async { Future<Map<String, dynamic>> _read() async {
final dirPath = await pathForWalletDir(name: walletInfo.name, type: walletInfo.type); final dirPath =
await pathForWalletDir(name: walletInfo.name, type: walletInfo.type, isFlatpak: isFlatpak);
final path = '$dirPath/$transactionsHistoryFileName'; final path = '$dirPath/$transactionsHistoryFileName';
final content = await encryptionFileUtils.read(path: path, password: _password); final content = await encryptionFileUtils.read(path: path, password: _password);
if (content.isEmpty) { if (content.isEmpty) {

View file

@ -40,6 +40,7 @@ abstract class NanoWalletBase
required String password, required String password,
NanoBalance? initialBalance, NanoBalance? initialBalance,
required EncryptionFileUtils encryptionFileUtils, required EncryptionFileUtils encryptionFileUtils,
required this.isFlatpak,
}) : syncStatus = NotConnectedSyncStatus(), }) : syncStatus = NotConnectedSyncStatus(),
_password = password, _password = password,
_mnemonic = mnemonic, _mnemonic = mnemonic,
@ -64,6 +65,8 @@ abstract class NanoWalletBase
} }
} }
final bool isFlatpak;
String _mnemonic; String _mnemonic;
final String _password; final String _password;
DerivationType _derivationType; DerivationType _derivationType;
@ -366,7 +369,8 @@ abstract class NanoWalletBase
} }
} }
Future<String> makePath() async => pathForWallet(name: walletInfo.name, type: walletInfo.type); Future<String> makePath() async =>
pathForWallet(name: walletInfo.name, type: walletInfo.type, isFlatpak: isFlatpak);
String toJSON() => json.encode({ String toJSON() => json.encode({
'seedKey': _hexSeed, 'seedKey': _hexSeed,
@ -376,13 +380,13 @@ abstract class NanoWalletBase
'derivationType': _derivationType.toString() 'derivationType': _derivationType.toString()
}); });
static Future<NanoWallet> open({ static Future<NanoWallet> open(
required String name, {required String name,
required String password, required String password,
required WalletInfo walletInfo, required WalletInfo walletInfo,
required EncryptionFileUtils encryptionFileUtils, required EncryptionFileUtils encryptionFileUtils,
}) async { required bool isFlatpak}) async {
final path = await pathForWallet(name: name, type: walletInfo.type); final path = await pathForWallet(name: name, type: walletInfo.type, isFlatpak: isFlatpak);
final jsonSource = await encryptionFileUtils.read(path: path, password: password); final jsonSource = await encryptionFileUtils.read(path: path, password: password);
final data = json.decode(jsonSource) as Map; final data = json.decode(jsonSource) as Map;
@ -482,19 +486,23 @@ abstract class NanoWalletBase
@override @override
Future<void> renameWalletFiles(String newWalletName) async { Future<void> renameWalletFiles(String newWalletName) async {
final currentWalletPath = await pathForWallet(name: walletInfo.name, type: type); final currentWalletPath =
await pathForWallet(name: walletInfo.name, type: type, isFlatpak: isFlatpak);
final currentWalletFile = File(currentWalletPath); final currentWalletFile = File(currentWalletPath);
final currentDirPath = await pathForWalletDir(name: walletInfo.name, type: type); final currentDirPath =
await pathForWalletDir(name: walletInfo.name, type: type, isFlatpak: isFlatpak);
final currentTransactionsFile = File('$currentDirPath/$transactionsHistoryFileName'); final currentTransactionsFile = File('$currentDirPath/$transactionsHistoryFileName');
// Copies current wallet files into new wallet name's dir and files // Copies current wallet files into new wallet name's dir and files
if (currentWalletFile.existsSync()) { if (currentWalletFile.existsSync()) {
final newWalletPath = await pathForWallet(name: newWalletName, type: type); final newWalletPath =
await pathForWallet(name: newWalletName, type: type, isFlatpak: isFlatpak);
await currentWalletFile.copy(newWalletPath); await currentWalletFile.copy(newWalletPath);
} }
if (currentTransactionsFile.existsSync()) { if (currentTransactionsFile.existsSync()) {
final newDirPath = await pathForWalletDir(name: newWalletName, type: type); final newDirPath =
await pathForWalletDir(name: newWalletName, type: type, isFlatpak: isFlatpak);
await currentTransactionsFile.copy('$newDirPath/$transactionsHistoryFileName'); await currentTransactionsFile.copy('$newDirPath/$transactionsHistoryFileName');
} }

View file

@ -16,10 +16,11 @@ import 'package:nanoutil/nanoutil.dart';
class NanoWalletService extends WalletService<NanoNewWalletCredentials, class NanoWalletService extends WalletService<NanoNewWalletCredentials,
NanoRestoreWalletFromSeedCredentials, NanoRestoreWalletFromKeysCredentials> { NanoRestoreWalletFromSeedCredentials, NanoRestoreWalletFromKeysCredentials> {
NanoWalletService(this.walletInfoSource, this.isDirect); NanoWalletService(this.walletInfoSource, this.isDirect, this.isFlatpak);
final Box<WalletInfo> walletInfoSource; final Box<WalletInfo> walletInfoSource;
final bool isDirect; final bool isDirect;
final bool isFlatpak;
static bool walletFilesExist(String path) => static bool walletFilesExist(String path) =>
!File(path).existsSync() && !File('$path.keys').existsSync(); !File(path).existsSync() && !File('$path.keys').existsSync();
@ -41,6 +42,7 @@ class NanoWalletService extends WalletService<NanoNewWalletCredentials,
mnemonic: mnemonic, mnemonic: mnemonic,
password: credentials.password!, password: credentials.password!,
encryptionFileUtils: encryptionFileUtilsFor(isDirect), encryptionFileUtils: encryptionFileUtilsFor(isDirect),
isFlatpak: isFlatpak,
); );
wallet.init(); wallet.init();
return wallet; return wallet;
@ -48,7 +50,7 @@ class NanoWalletService extends WalletService<NanoNewWalletCredentials,
@override @override
Future<void> remove(String wallet) async { Future<void> remove(String wallet) async {
final path = await pathForWalletDir(name: wallet, type: getType()); final path = await pathForWalletDir(name: wallet, type: getType(), isFlatpak: isFlatpak);
final file = Directory(path); final file = Directory(path);
final isExist = file.existsSync(); final isExist = file.existsSync();
@ -73,6 +75,7 @@ class NanoWalletService extends WalletService<NanoNewWalletCredentials,
password: password, password: password,
mnemonic: randomWords, mnemonic: randomWords,
encryptionFileUtils: encryptionFileUtilsFor(isDirect), encryptionFileUtils: encryptionFileUtilsFor(isDirect),
isFlatpak: isFlatpak,
); );
await currentWallet.renameWalletFiles(newName); await currentWallet.renameWalletFiles(newName);
@ -113,6 +116,7 @@ class NanoWalletService extends WalletService<NanoNewWalletCredentials,
mnemonic: mnemonic ?? credentials.seedKey, mnemonic: mnemonic ?? credentials.seedKey,
walletInfo: credentials.walletInfo!, walletInfo: credentials.walletInfo!,
encryptionFileUtils: encryptionFileUtilsFor(isDirect), encryptionFileUtils: encryptionFileUtilsFor(isDirect),
isFlatpak: isFlatpak,
); );
await wallet.init(); await wallet.init();
await wallet.save(); await wallet.save();
@ -144,6 +148,7 @@ class NanoWalletService extends WalletService<NanoNewWalletCredentials,
mnemonic: credentials.mnemonic, mnemonic: credentials.mnemonic,
walletInfo: credentials.walletInfo!, walletInfo: credentials.walletInfo!,
encryptionFileUtils: encryptionFileUtilsFor(isDirect), encryptionFileUtils: encryptionFileUtilsFor(isDirect),
isFlatpak: isFlatpak,
); );
await wallet.init(); await wallet.init();
@ -153,7 +158,7 @@ class NanoWalletService extends WalletService<NanoNewWalletCredentials,
@override @override
Future<bool> isWalletExit(String name) async => Future<bool> isWalletExit(String name) async =>
File(await pathForWallet(name: name, type: getType())).existsSync(); File(await pathForWallet(name: name, type: getType(), isFlatpak: isFlatpak)).existsSync();
@override @override
Future<NanoWallet> openWallet(String name, String password) async { Future<NanoWallet> openWallet(String name, String password) async {
@ -164,6 +169,7 @@ class NanoWalletService extends WalletService<NanoNewWalletCredentials,
password: password, password: password,
walletInfo: walletInfo, walletInfo: walletInfo,
encryptionFileUtils: encryptionFileUtilsFor(isDirect), encryptionFileUtils: encryptionFileUtilsFor(isDirect),
isFlatpak: isFlatpak,
); );
await wallet.init(); await wallet.init();

View file

@ -15,11 +15,13 @@ class PolygonTransactionHistory = PolygonTransactionHistoryBase with _$PolygonTr
abstract class PolygonTransactionHistoryBase extends TransactionHistoryBase<PolygonTransactionInfo> abstract class PolygonTransactionHistoryBase extends TransactionHistoryBase<PolygonTransactionInfo>
with Store { with Store {
PolygonTransactionHistoryBase({required this.walletInfo, required String password}) PolygonTransactionHistoryBase(
{required this.walletInfo, required String password, required this.isFlatpak})
: _password = password { : _password = password {
transactions = ObservableMap<String, PolygonTransactionInfo>(); transactions = ObservableMap<String, PolygonTransactionInfo>();
} }
final bool isFlatpak;
final WalletInfo walletInfo; final WalletInfo walletInfo;
String _password; String _password;
@ -28,7 +30,8 @@ abstract class PolygonTransactionHistoryBase extends TransactionHistoryBase<Poly
@override @override
Future<void> save() async { Future<void> save() async {
try { try {
final dirPath = await pathForWalletDir(name: walletInfo.name, type: walletInfo.type); final dirPath = await pathForWalletDir(
name: walletInfo.name, type: walletInfo.type, isFlatpak: isFlatpak);
final path = '$dirPath/$transactionsHistoryFileName'; final path = '$dirPath/$transactionsHistoryFileName';
final data = json.encode({'transactions': transactions}); final data = json.encode({'transactions': transactions});
await writeData(path: path, password: _password, data: data); await writeData(path: path, password: _password, data: data);
@ -46,7 +49,8 @@ abstract class PolygonTransactionHistoryBase extends TransactionHistoryBase<Poly
this.transactions.addAll(transactions); this.transactions.addAll(transactions);
Future<Map<String, dynamic>> _read() async { Future<Map<String, dynamic>> _read() async {
final dirPath = await pathForWalletDir(name: walletInfo.name, type: walletInfo.type); final dirPath =
await pathForWalletDir(name: walletInfo.name, type: walletInfo.type, isFlatpak: isFlatpak);
final path = '$dirPath/$transactionsHistoryFileName'; final path = '$dirPath/$transactionsHistoryFileName';
final content = await read(path: path, password: _password); final content = await read(path: path, password: _password);
if (content.isEmpty) { if (content.isEmpty) {

View file

@ -49,6 +49,7 @@ abstract class PolygonWalletBase
String? privateKey, String? privateKey,
required String password, required String password,
ERC20Balance? initialBalance, ERC20Balance? initialBalance,
required this.isFlatpak,
}) : syncStatus = const NotConnectedSyncStatus(), }) : syncStatus = const NotConnectedSyncStatus(),
_password = password, _password = password,
_mnemonic = mnemonic, _mnemonic = mnemonic,
@ -69,6 +70,8 @@ abstract class PolygonWalletBase
_sharedPrefs.complete(SharedPreferences.getInstance()); _sharedPrefs.complete(SharedPreferences.getInstance());
} }
final bool isFlatpak;
final String? _mnemonic; final String? _mnemonic;
final String? _hexPrivateKey; final String? _hexPrivateKey;
final String _password; final String _password;
@ -342,7 +345,8 @@ abstract class PolygonWalletBase
} }
} }
Future<String> makePath() async => pathForWallet(name: walletInfo.name, type: walletInfo.type); Future<String> makePath() async =>
pathForWallet(name: walletInfo.name, type: walletInfo.type, isFlatpak: isFlatpak);
String toJSON() => json.encode({ String toJSON() => json.encode({
'mnemonic': _mnemonic, 'mnemonic': _mnemonic,
@ -350,12 +354,12 @@ abstract class PolygonWalletBase
'balance': balance[currency]!.toJSON(), 'balance': balance[currency]!.toJSON(),
}); });
static Future<PolygonWallet> open({ static Future<PolygonWallet> open(
required String name, {required String name,
required String password, required String password,
required WalletInfo walletInfo, required WalletInfo walletInfo,
}) async { required bool isFlatpak}) async {
final path = await pathForWallet(name: name, type: walletInfo.type); final path = await pathForWallet(name: name, type: walletInfo.type, isFlatpak: isFlatpak);
final jsonSource = await read(path: path, password: password); final jsonSource = await read(path: path, password: password);
final data = json.decode(jsonSource) as Map; final data = json.decode(jsonSource) as Map;
final mnemonic = data['mnemonic'] as String?; final mnemonic = data['mnemonic'] as String?;
@ -477,19 +481,23 @@ abstract class PolygonWalletBase
@override @override
Future<void> renameWalletFiles(String newWalletName) async { Future<void> renameWalletFiles(String newWalletName) async {
final currentWalletPath = await pathForWallet(name: walletInfo.name, type: type); final currentWalletPath =
await pathForWallet(name: walletInfo.name, type: type, isFlatpak: isFlatpak);
final currentWalletFile = File(currentWalletPath); final currentWalletFile = File(currentWalletPath);
final currentDirPath = await pathForWalletDir(name: walletInfo.name, type: type); final currentDirPath =
await pathForWalletDir(name: walletInfo.name, type: type, isFlatpak: isFlatpak);
final currentTransactionsFile = File('$currentDirPath/$transactionsHistoryFileName'); final currentTransactionsFile = File('$currentDirPath/$transactionsHistoryFileName');
// Copies current wallet files into new wallet name's dir and files // Copies current wallet files into new wallet name's dir and files
if (currentWalletFile.existsSync()) { if (currentWalletFile.existsSync()) {
final newWalletPath = await pathForWallet(name: newWalletName, type: type); final newWalletPath =
await pathForWallet(name: newWalletName, type: type, isFlatpak: isFlatpak);
await currentWalletFile.copy(newWalletPath); await currentWalletFile.copy(newWalletPath);
} }
if (currentTransactionsFile.existsSync()) { if (currentTransactionsFile.existsSync()) {
final newDirPath = await pathForWalletDir(name: newWalletName, type: type); final newDirPath =
await pathForWalletDir(name: newWalletName, type: type, isFlatpak: isFlatpak);
await currentTransactionsFile.copy('$newDirPath/$transactionsHistoryFileName'); await currentTransactionsFile.copy('$newDirPath/$transactionsHistoryFileName');
} }

View file

@ -14,9 +14,10 @@ import 'package:collection/collection.dart';
class PolygonWalletService extends WalletService<PolygonNewWalletCredentials, class PolygonWalletService extends WalletService<PolygonNewWalletCredentials,
PolygonRestoreWalletFromSeedCredentials, PolygonRestoreWalletFromPrivateKey> { PolygonRestoreWalletFromSeedCredentials, PolygonRestoreWalletFromPrivateKey> {
PolygonWalletService(this.walletInfoSource); PolygonWalletService(this.walletInfoSource, this.isFlatpak);
final Box<WalletInfo> walletInfoSource; final Box<WalletInfo> walletInfoSource;
final bool isFlatpak;
@override @override
Future<PolygonWallet> create(PolygonNewWalletCredentials credentials) async { Future<PolygonWallet> create(PolygonNewWalletCredentials credentials) async {
@ -27,6 +28,7 @@ class PolygonWalletService extends WalletService<PolygonNewWalletCredentials,
walletInfo: credentials.walletInfo!, walletInfo: credentials.walletInfo!,
mnemonic: mnemonic, mnemonic: mnemonic,
password: credentials.password!, password: credentials.password!,
isFlatpak: isFlatpak,
); );
await wallet.init(); await wallet.init();
@ -41,7 +43,7 @@ class PolygonWalletService extends WalletService<PolygonNewWalletCredentials,
@override @override
Future<bool> isWalletExit(String name) async => Future<bool> isWalletExit(String name) async =>
File(await pathForWallet(name: name, type: getType())).existsSync(); File(await pathForWallet(name: name, type: getType(), isFlatpak: isFlatpak)).existsSync();
@override @override
Future<PolygonWallet> openWallet(String name, String password) async { Future<PolygonWallet> openWallet(String name, String password) async {
@ -51,6 +53,7 @@ class PolygonWalletService extends WalletService<PolygonNewWalletCredentials,
name: name, name: name,
password: password, password: password,
walletInfo: walletInfo, walletInfo: walletInfo,
isFlatpak: isFlatpak,
); );
await wallet.init(); await wallet.init();
@ -61,7 +64,8 @@ class PolygonWalletService extends WalletService<PolygonNewWalletCredentials,
@override @override
Future<void> remove(String wallet) async { Future<void> remove(String wallet) async {
File(await pathForWalletDir(name: wallet, type: getType())).delete(recursive: true); File(await pathForWalletDir(name: wallet, type: getType(), isFlatpak: isFlatpak))
.delete(recursive: true);
final walletInfo = walletInfoSource.values final walletInfo = walletInfoSource.values
.firstWhereOrNull((info) => info.id == WalletBase.idFor(wallet, getType()))!; .firstWhereOrNull((info) => info.id == WalletBase.idFor(wallet, getType()))!;
await walletInfoSource.delete(walletInfo.key); await walletInfoSource.delete(walletInfo.key);
@ -73,6 +77,7 @@ class PolygonWalletService extends WalletService<PolygonNewWalletCredentials,
password: credentials.password!, password: credentials.password!,
privateKey: credentials.privateKey, privateKey: credentials.privateKey,
walletInfo: credentials.walletInfo!, walletInfo: credentials.walletInfo!,
isFlatpak: isFlatpak,
); );
await wallet.init(); await wallet.init();
@ -92,6 +97,7 @@ class PolygonWalletService extends WalletService<PolygonNewWalletCredentials,
password: credentials.password!, password: credentials.password!,
mnemonic: credentials.mnemonic, mnemonic: credentials.mnemonic,
walletInfo: credentials.walletInfo!, walletInfo: credentials.walletInfo!,
isFlatpak: isFlatpak,
); );
await wallet.init(); await wallet.init();
@ -106,7 +112,11 @@ class PolygonWalletService extends WalletService<PolygonNewWalletCredentials,
final currentWalletInfo = walletInfoSource.values final currentWalletInfo = walletInfoSource.values
.firstWhere((info) => info.id == WalletBase.idFor(currentName, getType())); .firstWhere((info) => info.id == WalletBase.idFor(currentName, getType()));
final currentWallet = await PolygonWalletBase.open( final currentWallet = await PolygonWalletBase.open(
password: password, name: currentName, walletInfo: currentWalletInfo); password: password,
name: currentName,
walletInfo: currentWalletInfo,
isFlatpak: isFlatpak,
);
await currentWallet.renameWalletFiles(newName); await currentWallet.renameWalletFiles(newName);

View file

@ -1,182 +1,286 @@
part of 'bitcoin.dart'; part of 'bitcoin.dart';
class CWBitcoin extends Bitcoin { class CWBitcoin extends Bitcoin {
@override @override
TransactionPriority getMediumTransactionPriority() => BitcoinTransactionPriority.medium; TransactionPriority getMediumTransactionPriority() => BitcoinTransactionPriority.medium;
@override
WalletCredentials createBitcoinRestoreWalletFromSeedCredentials({
required String name,
required String mnemonic,
required String password})
=> BitcoinRestoreWalletFromSeedCredentials(name: name, mnemonic: mnemonic, password: password);
@override
WalletCredentials createBitcoinRestoreWalletFromWIFCredentials({
required String name,
required String password,
required String wif,
WalletInfo? walletInfo})
=> BitcoinRestoreWalletFromWIFCredentials(name: name, password: password, wif: wif, walletInfo: walletInfo);
@override
WalletCredentials createBitcoinNewWalletCredentials({
required String name,
WalletInfo? walletInfo,
String? password})
=> BitcoinNewWalletCredentials(name: name, walletInfo: walletInfo, password: password);
@override
List<String> getWordList() => wordlist;
@override
Map<String, String> getWalletKeys(Object wallet) {
final bitcoinWallet = wallet as ElectrumWallet;
final keys = bitcoinWallet.keys;
return <String, String>{
'wif': keys.wif,
'privateKey': keys.privateKey,
'publicKey': keys.publicKey
};
}
@override
List<TransactionPriority> getTransactionPriorities()
=> BitcoinTransactionPriority.all;
@override
List<TransactionPriority> getLitecoinTransactionPriorities()
=> LitecoinTransactionPriority.all;
@override
TransactionPriority deserializeBitcoinTransactionPriority(int raw)
=> BitcoinTransactionPriority.deserialize(raw: raw);
@override
TransactionPriority deserializeLitecoinTransactionPriority(int raw)
=> LitecoinTransactionPriority.deserialize(raw: raw);
@override
int getFeeRate(Object wallet, TransactionPriority priority) {
final bitcoinWallet = wallet as ElectrumWallet;
return bitcoinWallet.feeRate(priority);
}
@override
Future<void> generateNewAddress(Object wallet, String label) async {
final bitcoinWallet = wallet as ElectrumWallet;
await bitcoinWallet.walletAddresses.generateNewAddress(label: label);
await wallet.save();
}
@override
Future<void> updateAddress(Object wallet,String address, String label) async {
final bitcoinWallet = wallet as ElectrumWallet;
bitcoinWallet.walletAddresses.updateAddress(address, label);
await wallet.save();
}
@override
Object createBitcoinTransactionCredentials(List<Output> outputs, {required TransactionPriority priority, int? feeRate})
=> BitcoinTransactionCredentials(
outputs.map((out) => OutputInfo(
fiatAmount: out.fiatAmount,
cryptoAmount: out.cryptoAmount,
address: out.address,
note: out.note,
sendAll: out.sendAll,
extractedAddress: out.extractedAddress,
isParsedAddress: out.isParsedAddress,
formattedCryptoAmount: out.formattedCryptoAmount))
.toList(),
priority: priority as BitcoinTransactionPriority,
feeRate: feeRate);
@override
Object createBitcoinTransactionCredentialsRaw(List<OutputInfo> outputs, {TransactionPriority? priority, required int feeRate})
=> BitcoinTransactionCredentials(
outputs,
priority: priority != null ? priority as BitcoinTransactionPriority : null,
feeRate: feeRate);
@override
List<String> getAddresses(Object wallet) {
final bitcoinWallet = wallet as ElectrumWallet;
return bitcoinWallet.walletAddresses.addresses
.map((BitcoinAddressRecord addr) => addr.address)
.toList();
}
@override
@computed
List<ElectrumSubAddress> getSubAddresses(Object wallet) {
final electrumWallet = wallet as ElectrumWallet;
return electrumWallet.walletAddresses.addresses
.map((BitcoinAddressRecord addr) => ElectrumSubAddress(
id: addr.index,
name: addr.name,
address: electrumWallet.type == WalletType.bitcoinCash ? addr.cashAddr : addr.address,
txCount: addr.txCount,
balance: addr.balance,
isChange: addr.isHidden))
.toList();
}
@override
String getAddress(Object wallet) {
final bitcoinWallet = wallet as ElectrumWallet;
return bitcoinWallet.walletAddresses.address;
}
@override
String formatterBitcoinAmountToString({required int amount})
=> bitcoinAmountToString(amount: amount);
@override
double formatterBitcoinAmountToDouble({required int amount})
=> bitcoinAmountToDouble(amount: amount);
@override
int formatterStringDoubleToBitcoinAmount(String amount)
=> stringDoubleToBitcoinAmount(amount);
@override @override
String bitcoinTransactionPriorityWithLabel(TransactionPriority priority, int rate) WalletCredentials createBitcoinRestoreWalletFromSeedCredentials(
=> (priority as BitcoinTransactionPriority).labelWithRate(rate); {required String name, required String mnemonic, required String password}) =>
BitcoinRestoreWalletFromSeedCredentials(name: name, mnemonic: mnemonic, password: password);
@override
List<BitcoinUnspent> getUnspents(Object wallet) {
final bitcoinWallet = wallet as ElectrumWallet;
return bitcoinWallet.unspentCoins;
}
void updateUnspents(Object wallet) async {
final bitcoinWallet = wallet as ElectrumWallet;
await bitcoinWallet.updateUnspent();
}
WalletService createBitcoinWalletService(Box<WalletInfo> walletInfoSource, Box<UnspentCoinsInfo> unspentCoinSource, bool isDirect) {
return BitcoinWalletService(walletInfoSource, unspentCoinSource, isDirect);
}
WalletService createLitecoinWalletService(Box<WalletInfo> walletInfoSource, Box<UnspentCoinsInfo> unspentCoinSource, bool isDirect) {
return LitecoinWalletService(walletInfoSource, unspentCoinSource, isDirect);
}
@override @override
TransactionPriority getBitcoinTransactionPriorityMedium() WalletCredentials createBitcoinRestoreWalletFromWIFCredentials(
=> BitcoinTransactionPriority.medium; {required String name,
required String password,
required String wif,
WalletInfo? walletInfo}) =>
BitcoinRestoreWalletFromWIFCredentials(
name: name, password: password, wif: wif, walletInfo: walletInfo);
@override @override
TransactionPriority getLitecoinTransactionPriorityMedium() WalletCredentials createBitcoinNewWalletCredentials(
=> LitecoinTransactionPriority.medium; {required String name, WalletInfo? walletInfo, String? password}) =>
BitcoinNewWalletCredentials(name: name, walletInfo: walletInfo, password: password);
@override @override
TransactionPriority getBitcoinTransactionPrioritySlow() List<String> getWordList() => wordlist;
=> BitcoinTransactionPriority.slow;
@override @override
TransactionPriority getLitecoinTransactionPrioritySlow() Map<String, String> getWalletKeys(Object wallet) {
=> LitecoinTransactionPriority.slow; final bitcoinWallet = wallet as ElectrumWallet;
final keys = bitcoinWallet.keys;
return <String, String>{
'wif': keys.wif,
'privateKey': keys.privateKey,
'publicKey': keys.publicKey
};
}
@override
List<TransactionPriority> getTransactionPriorities() => BitcoinTransactionPriority.all;
@override
List<TransactionPriority> getLitecoinTransactionPriorities() => LitecoinTransactionPriority.all;
@override
TransactionPriority deserializeBitcoinTransactionPriority(int raw) =>
BitcoinTransactionPriority.deserialize(raw: raw);
@override
TransactionPriority deserializeLitecoinTransactionPriority(int raw) =>
LitecoinTransactionPriority.deserialize(raw: raw);
@override
int getFeeRate(Object wallet, TransactionPriority priority) {
final bitcoinWallet = wallet as ElectrumWallet;
return bitcoinWallet.feeRate(priority);
}
@override
Future<void> generateNewAddress(Object wallet, String label) async {
final bitcoinWallet = wallet as ElectrumWallet;
await bitcoinWallet.walletAddresses.generateNewAddress(label: label);
await wallet.save();
}
@override
Future<void> updateAddress(Object wallet, String address, String label) async {
final bitcoinWallet = wallet as ElectrumWallet;
bitcoinWallet.walletAddresses.updateAddress(address, label);
await wallet.save();
}
@override
Object createBitcoinTransactionCredentials(List<Output> outputs,
{required TransactionPriority priority, int? feeRate}) =>
BitcoinTransactionCredentials(
outputs
.map((out) => OutputInfo(
fiatAmount: out.fiatAmount,
cryptoAmount: out.cryptoAmount,
address: out.address,
note: out.note,
sendAll: out.sendAll,
extractedAddress: out.extractedAddress,
isParsedAddress: out.isParsedAddress,
formattedCryptoAmount: out.formattedCryptoAmount))
.toList(),
priority: priority as BitcoinTransactionPriority,
feeRate: feeRate);
@override
Object createBitcoinTransactionCredentialsRaw(List<OutputInfo> outputs,
{TransactionPriority? priority, required int feeRate}) =>
BitcoinTransactionCredentials(outputs,
priority: priority != null ? priority as BitcoinTransactionPriority : null,
feeRate: feeRate);
@override
List<String> getAddresses(Object wallet) {
final bitcoinWallet = wallet as ElectrumWallet;
return bitcoinWallet.walletAddresses.addresses
.map((BitcoinAddressRecord addr) => addr.address)
.toList();
}
@override
@computed
List<ElectrumSubAddress> getSubAddresses(Object wallet) {
final electrumWallet = wallet as ElectrumWallet;
return electrumWallet.walletAddresses.addresses
.map((BitcoinAddressRecord addr) => ElectrumSubAddress(
id: addr.index,
name: addr.name,
address: electrumWallet.type == WalletType.bitcoinCash ? addr.cashAddr : addr.address,
txCount: addr.txCount,
balance: addr.balance,
isChange: addr.isHidden))
.toList();
}
@override
String getAddress(Object wallet) {
final bitcoinWallet = wallet as ElectrumWallet;
return bitcoinWallet.walletAddresses.address;
}
@override
String formatterBitcoinAmountToString({required int amount}) =>
bitcoinAmountToString(amount: amount);
@override
double formatterBitcoinAmountToDouble({required int amount}) =>
bitcoinAmountToDouble(amount: amount);
@override
int formatterStringDoubleToBitcoinAmount(String amount) => stringDoubleToBitcoinAmount(amount);
@override
WalletCredentials createBitcoinRestoreWalletFromSeedCredentials(
{required String name, required String mnemonic, required String password}) =>
BitcoinRestoreWalletFromSeedCredentials(name: name, mnemonic: mnemonic, password: password);
@override
WalletCredentials createBitcoinRestoreWalletFromWIFCredentials(
{required String name,
required String password,
required String wif,
WalletInfo? walletInfo}) =>
BitcoinRestoreWalletFromWIFCredentials(
name: name, password: password, wif: wif, walletInfo: walletInfo);
@override
WalletCredentials createBitcoinNewWalletCredentials(
{required String name, WalletInfo? walletInfo, String? password}) =>
BitcoinNewWalletCredentials(name: name, walletInfo: walletInfo, password: password);
@override
List<String> getWordList() => wordlist;
@override
Map<String, String> getWalletKeys(Object wallet) {
final bitcoinWallet = wallet as ElectrumWallet;
final keys = bitcoinWallet.keys;
return <String, String>{
'wif': keys.wif,
'privateKey': keys.privateKey,
'publicKey': keys.publicKey
};
}
@override
List<TransactionPriority> getTransactionPriorities() => BitcoinTransactionPriority.all;
@override
List<TransactionPriority> getLitecoinTransactionPriorities() => LitecoinTransactionPriority.all;
@override
TransactionPriority deserializeBitcoinTransactionPriority(int raw) =>
BitcoinTransactionPriority.deserialize(raw: raw);
@override
TransactionPriority deserializeLitecoinTransactionPriority(int raw) =>
LitecoinTransactionPriority.deserialize(raw: raw);
@override
int getFeeRate(Object wallet, TransactionPriority priority) {
final bitcoinWallet = wallet as ElectrumWallet;
return bitcoinWallet.feeRate(priority);
}
@override
Future<void> generateNewAddress(Object wallet) async {
final bitcoinWallet = wallet as ElectrumWallet;
await bitcoinWallet.walletAddresses.generateNewAddress();
}
@override
Object createBitcoinTransactionCredentials(List<Output> outputs,
{required TransactionPriority priority, int? feeRate}) =>
BitcoinTransactionCredentials(
outputs
.map((out) => OutputInfo(
fiatAmount: out.fiatAmount,
cryptoAmount: out.cryptoAmount,
address: out.address,
note: out.note,
sendAll: out.sendAll,
extractedAddress: out.extractedAddress,
isParsedAddress: out.isParsedAddress,
formattedCryptoAmount: out.formattedCryptoAmount))
.toList(),
priority: priority as BitcoinTransactionPriority,
feeRate: feeRate);
@override
Object createBitcoinTransactionCredentialsRaw(List<OutputInfo> outputs,
{TransactionPriority? priority, required int feeRate}) =>
BitcoinTransactionCredentials(outputs,
priority: priority != null ? priority as BitcoinTransactionPriority : null,
feeRate: feeRate);
@override
List<String> getAddresses(Object wallet) {
final bitcoinWallet = wallet as ElectrumWallet;
return bitcoinWallet.walletAddresses.addresses
.map((BitcoinAddressRecord addr) => addr.address)
.toList();
}
@override
String getAddress(Object wallet) {
final bitcoinWallet = wallet as ElectrumWallet;
return bitcoinWallet.walletAddresses.address;
}
@override
String formatterBitcoinAmountToString({required int amount}) =>
bitcoinAmountToString(amount: amount);
@override
double formatterBitcoinAmountToDouble({required int amount}) =>
bitcoinAmountToDouble(amount: amount);
@override
int formatterStringDoubleToBitcoinAmount(String amount) => stringDoubleToBitcoinAmount(amount);
@override
String bitcoinTransactionPriorityWithLabel(TransactionPriority priority, int rate) =>
(priority as BitcoinTransactionPriority).labelWithRate(rate);
@override
List<BitcoinUnspent> getUnspents(Object wallet) {
final bitcoinWallet = wallet as ElectrumWallet;
return bitcoinWallet.unspentCoins;
}
void updateUnspents(Object wallet) async {
final bitcoinWallet = wallet as ElectrumWallet;
await bitcoinWallet.updateUnspent();
}
WalletService createBitcoinWalletService(Box<WalletInfo> walletInfoSource,
Box<UnspentCoinsInfo> unspentCoinSource, bool isDirect, bool isFlatpak) {
return BitcoinWalletService(walletInfoSource, unspentCoinSource, isDirect, isFlatpak);
}
WalletService createLitecoinWalletService(Box<WalletInfo> walletInfoSource,
Box<UnspentCoinsInfo> unspentCoinSource, bool isDirect, bool isFlatpak) {
return LitecoinWalletService(walletInfoSource, unspentCoinSource, isDirect, isFlatpak);
}
@override
TransactionPriority getBitcoinTransactionPriorityMedium() => BitcoinTransactionPriority.medium;
@override
TransactionPriority getLitecoinTransactionPriorityMedium() => LitecoinTransactionPriority.medium;
@override
TransactionPriority getBitcoinTransactionPrioritySlow() => BitcoinTransactionPriority.slow;
@override
TransactionPriority getLitecoinTransactionPrioritySlow() => LitecoinTransactionPriority.slow;
} }

View file

@ -11,9 +11,9 @@ class CWBitcoinCash extends BitcoinCash {
String getCashAddrFormat(String address) => AddressUtils.getCashAddrFormat(address); String getCashAddrFormat(String address) => AddressUtils.getCashAddrFormat(address);
@override @override
WalletService createBitcoinCashWalletService( WalletService createBitcoinCashWalletService(Box<WalletInfo> walletInfoSource,
Box<WalletInfo> walletInfoSource, Box<UnspentCoinsInfo> unspentCoinSource, bool isDirect) { Box<UnspentCoinsInfo> unspentCoinSource, bool isDirect, bool isFlatpak) {
return BitcoinCashWalletService(walletInfoSource, unspentCoinSource, isDirect); return BitcoinCashWalletService(walletInfoSource, unspentCoinSource, isDirect, isFlatpak);
} }
@override @override

View file

@ -8,7 +8,6 @@ import 'package:cw_core/wallet_type.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:hive/hive.dart'; import 'package:hive/hive.dart';
import 'package:cake_wallet/core/secure_storage.dart'; import 'package:cake_wallet/core/secure_storage.dart';
import 'package:path_provider/path_provider.dart';
import 'package:cryptography/cryptography.dart'; import 'package:cryptography/cryptography.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import 'package:archive/archive_io.dart'; import 'package:archive/archive_io.dart';
@ -21,6 +20,7 @@ import 'package:cw_core/wallet_info.dart';
import 'package:cake_wallet/.secrets.g.dart' as secrets; import 'package:cake_wallet/.secrets.g.dart' as secrets;
import 'package:cake_wallet/wallet_types.g.dart'; import 'package:cake_wallet/wallet_types.g.dart';
import 'package:cake_backup/backup.dart' as cake_backup; import 'package:cake_backup/backup.dart' as cake_backup;
import 'package:cake_wallet/core/flatpak.dart';
class BackupService { class BackupService {
BackupService( BackupService(
@ -76,7 +76,7 @@ class BackupService {
Future<Uint8List> _exportBackupV2(String password) async { Future<Uint8List> _exportBackupV2(String password) async {
final zipEncoder = ZipFileEncoder(); final zipEncoder = ZipFileEncoder();
final appDir = await getAppDir(); final appDir = await getAppDir(isFlatpak: isFlatpak);
final now = DateTime.now(); final now = DateTime.now();
final tmpDir = Directory('${appDir.path}/~_BACKUP_TMP'); final tmpDir = Directory('${appDir.path}/~_BACKUP_TMP');
final archivePath = '${tmpDir.path}/backup_${now.toString()}.zip'; final archivePath = '${tmpDir.path}/backup_${now.toString()}.zip';
@ -115,9 +115,8 @@ class BackupService {
return await _encryptV2(content, password); return await _encryptV2(content, password);
} }
Future<void> _importBackupV1(Uint8List data, String password, Future<void> _importBackupV1(Uint8List data, String password, {required String nonce}) async {
{required String nonce}) async { final appDir = await getAppDir(isFlatpak: isFlatpak);
final appDir = await getAppDir();
final decryptedData = await _decryptV1(data, password, nonce); final decryptedData = await _decryptV1(data, password, nonce);
final zip = ZipDecoder().decodeBytes(decryptedData); final zip = ZipDecoder().decodeBytes(decryptedData);
@ -140,7 +139,7 @@ class BackupService {
} }
Future<void> _importBackupV2(Uint8List data, String password) async { Future<void> _importBackupV2(Uint8List data, String password) async {
final appDir = await getAppDir(); final appDir = await getAppDir(isFlatpak: isFlatpak);
final decryptedData = await _decryptV2(data, password); final decryptedData = await _decryptV2(data, password);
final zip = ZipDecoder().decodeBytes(decryptedData); final zip = ZipDecoder().decodeBytes(decryptedData);
@ -173,7 +172,7 @@ class BackupService {
} }
Future<Box<WalletInfo>> _reloadHiveWalletInfoBox() async { Future<Box<WalletInfo>> _reloadHiveWalletInfoBox() async {
final appDir = await getAppDir(); final appDir = await getAppDir(isFlatpak: isFlatpak);
await CakeHive.close(); await CakeHive.close();
CakeHive.init(appDir.path); CakeHive.init(appDir.path);
@ -185,7 +184,7 @@ class BackupService {
} }
Future<void> _importPreferencesDump() async { Future<void> _importPreferencesDump() async {
final appDir = await getAppDir(); final appDir = await getAppDir(isFlatpak: isFlatpak);
final preferencesFile = File('${appDir.path}/~_preferences_dump'); final preferencesFile = File('${appDir.path}/~_preferences_dump');
if (!preferencesFile.existsSync()) { if (!preferencesFile.existsSync()) {
@ -361,9 +360,8 @@ class BackupService {
} }
Future<void> _importKeychainDumpV1(String password, Future<void> _importKeychainDumpV1(String password,
{required String nonce, {required String nonce, String keychainSalt = secrets.backupKeychainSalt}) async {
String keychainSalt = secrets.backupKeychainSalt}) async { final appDir = await getAppDir(isFlatpak: isFlatpak);
final appDir = await getAppDir();
final keychainDumpFile = File('${appDir.path}/~_keychain_dump'); final keychainDumpFile = File('${appDir.path}/~_keychain_dump');
final decryptedKeychainDumpFileData = final decryptedKeychainDumpFileData =
await _decryptV1(keychainDumpFile.readAsBytesSync(), '$keychainSalt$password', nonce); await _decryptV1(keychainDumpFile.readAsBytesSync(), '$keychainSalt$password', nonce);
@ -391,7 +389,7 @@ class BackupService {
Future<void> _importKeychainDumpV2(String password, Future<void> _importKeychainDumpV2(String password,
{String keychainSalt = secrets.backupKeychainSalt}) async { {String keychainSalt = secrets.backupKeychainSalt}) async {
final appDir = await getAppDir(); final appDir = await getAppDir(isFlatpak: isFlatpak);
final keychainDumpFile = File('${appDir.path}/~_keychain_dump'); final keychainDumpFile = File('${appDir.path}/~_keychain_dump');
final decryptedKeychainDumpFileData = final decryptedKeychainDumpFileData =
await _decryptV2(keychainDumpFile.readAsBytesSync(), '$keychainSalt$password'); await _decryptV2(keychainDumpFile.readAsBytesSync(), '$keychainSalt$password');

View file

@ -1,4 +1,5 @@
import 'package:cake_wallet/buy/onramper/onramper_buy_provider.dart'; import 'package:cake_wallet/buy/onramper/onramper_buy_provider.dart';
import 'package:cake_wallet/core/flatpak.dart';
import 'package:cake_wallet/core/yat_service.dart'; import 'package:cake_wallet/core/yat_service.dart';
import 'package:cake_wallet/entities/parse_address_from_domain.dart'; import 'package:cake_wallet/entities/parse_address_from_domain.dart';
import 'package:cake_wallet/entities/preferences_key.dart'; import 'package:cake_wallet/entities/preferences_key.dart';
@ -862,25 +863,26 @@ Future<void> setup({
case WalletType.haven: case WalletType.haven:
return haven!.createHavenWalletService(_walletInfoSource); return haven!.createHavenWalletService(_walletInfoSource);
case WalletType.monero: case WalletType.monero:
return monero!.createMoneroWalletService(_walletInfoSource, _unspentCoinsInfoSource); return monero!
.createMoneroWalletService(_walletInfoSource, _unspentCoinsInfoSource, isFlatpak);
case WalletType.bitcoin: case WalletType.bitcoin:
return bitcoin!.createBitcoinWalletService(_walletInfoSource, _unspentCoinsInfoSource, return bitcoin!.createBitcoinWalletService(_walletInfoSource, _unspentCoinsInfoSource,
SettingsStoreBase.walletPasswordDirectInput); SettingsStoreBase.walletPasswordDirectInput, isFlatpak);
case WalletType.litecoin: case WalletType.litecoin:
return bitcoin!.createLitecoinWalletService(_walletInfoSource, _unspentCoinsInfoSource, return bitcoin!.createLitecoinWalletService(_walletInfoSource, _unspentCoinsInfoSource,
SettingsStoreBase.walletPasswordDirectInput); SettingsStoreBase.walletPasswordDirectInput, isFlatpak);
case WalletType.ethereum: case WalletType.ethereum:
return ethereum!.createEthereumWalletService( return ethereum!.createEthereumWalletService(
_walletInfoSource, SettingsStoreBase.walletPasswordDirectInput); _walletInfoSource, SettingsStoreBase.walletPasswordDirectInput, isFlatpak);
case WalletType.bitcoinCash: case WalletType.bitcoinCash:
return bitcoinCash!.createBitcoinCashWalletService(_walletInfoSource, _unspentCoinsInfoSource, return bitcoinCash!.createBitcoinCashWalletService(_walletInfoSource,
SettingsStoreBase.walletPasswordDirectInput); _unspentCoinsInfoSource, SettingsStoreBase.walletPasswordDirectInput, isFlatpak);
case WalletType.nano: case WalletType.nano:
return nano!.createNanoWalletService( return nano!.createNanoWalletService(
_walletInfoSource, SettingsStoreBase.walletPasswordDirectInput); _walletInfoSource, SettingsStoreBase.walletPasswordDirectInput, isFlatpak);
case WalletType.polygon: case WalletType.polygon:
return polygon!.createPolygonWalletService( return polygon!.createPolygonWalletService(
_walletInfoSource, SettingsStoreBase.walletPasswordDirectInput); _walletInfoSource, SettingsStoreBase.walletPasswordDirectInput, isFlatpak);
default: default:
throw Exception('Unexpected token: ${param1.toString()} for generating of WalletService'); throw Exception('Unexpected token: ${param1.toString()} for generating of WalletService');
} }

View file

@ -20,6 +20,7 @@ import 'package:cw_core/wallet_info.dart';
import 'package:cake_wallet/exchange/trade.dart'; import 'package:cake_wallet/exchange/trade.dart';
import 'package:encrypt/encrypt.dart' as encrypt; import 'package:encrypt/encrypt.dart' as encrypt;
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:cake_wallet/core/flatpak.dart';
const newCakeWalletMoneroUri = 'xmr-node.cakewallet.com:18081'; const newCakeWalletMoneroUri = 'xmr-node.cakewallet.com:18081';
const cakeWalletBitcoinElectrumUri = 'electrum.cakewallet.com:50002'; const cakeWalletBitcoinElectrumUri = 'electrum.cakewallet.com:50002';
@ -204,7 +205,7 @@ Future<void> defaultSettingsMigration(
Future<void> _validateWalletInfoBoxData(Box<WalletInfo> walletInfoSource) async { Future<void> _validateWalletInfoBoxData(Box<WalletInfo> walletInfoSource) async {
try { try {
final root = await getAppDir(); final root = await getAppDir(isFlatpak: isFlatpak);
for (var type in WalletType.values) { for (var type in WalletType.values) {
if (type == WalletType.none) { if (type == WalletType.none) {
@ -223,7 +224,7 @@ Future<void> _validateWalletInfoBoxData(Box<WalletInfo> walletInfoSource) async
for (var name in walletNames) { for (var name in walletNames) {
final Directory dir; final Directory dir;
try { try {
dir = Directory(await pathForWalletDir(name: name, type: type)); dir = Directory(await pathForWalletDir(name: name, type: type, isFlatpak: isFlatpak));
} catch (_) { } catch (_) {
continue; continue;
} }
@ -588,7 +589,7 @@ Future<void> addAddressesForMoneroWallets(Box<WalletInfo> walletInfoSource) asyn
final moneroWalletsInfo = walletInfoSource.values.where((info) => info.type == WalletType.monero); final moneroWalletsInfo = walletInfoSource.values.where((info) => info.type == WalletType.monero);
moneroWalletsInfo.forEach((info) async { moneroWalletsInfo.forEach((info) async {
try { try {
final walletPath = await pathForWallet(name: info.name, type: WalletType.monero); final walletPath = await pathForWallet(name: info.name, type: WalletType.monero, isFlatpak: isFlatpak);
final addressFilePath = '$walletPath.address.txt'; final addressFilePath = '$walletPath.address.txt';
final addressFile = File(addressFilePath); final addressFile = File(addressFilePath);

View file

@ -4,8 +4,9 @@ class CWEthereum extends Ethereum {
@override @override
List<String> getEthereumWordList(String language) => EthereumMnemonics.englishWordlist; List<String> getEthereumWordList(String language) => EthereumMnemonics.englishWordlist;
WalletService createEthereumWalletService(Box<WalletInfo> walletInfoSource, bool isDirect) => WalletService createEthereumWalletService(
EthereumWalletService(walletInfoSource, isDirect); Box<WalletInfo> walletInfoSource, bool isDirect, bool isFlatpak) =>
EthereumWalletService(walletInfoSource, isDirect, isFlatpak);
@override @override
WalletCredentials createEthereumNewWalletCredentials({ WalletCredentials createEthereumNewWalletCredentials({

View file

@ -10,14 +10,12 @@ import 'package:cake_wallet/utils/exception_handler.dart';
import 'package:cw_core/address_info.dart'; import 'package:cw_core/address_info.dart';
import 'package:cake_wallet/utils/responsive_layout_util.dart'; import 'package:cake_wallet/utils/responsive_layout_util.dart';
import 'package:cw_core/root_dir.dart'; import 'package:cw_core/root_dir.dart';
import 'package:cake_wallet/utils/responsive_layout_util.dart';
import 'package:cw_core/hive_type_ids.dart'; import 'package:cw_core/hive_type_ids.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:hive/hive.dart'; import 'package:hive/hive.dart';
import 'package:cake_wallet/di.dart'; import 'package:cake_wallet/di.dart';
import 'package:path_provider/path_provider.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import 'package:cake_wallet/core/secure_storage.dart'; import 'package:cake_wallet/core/secure_storage.dart';
import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:flutter_mobx/flutter_mobx.dart';
@ -43,6 +41,7 @@ import 'package:uni_links/uni_links.dart';
import 'package:cw_core/unspent_coins_info.dart'; import 'package:cw_core/unspent_coins_info.dart';
import 'package:cake_wallet/monero/monero.dart'; import 'package:cake_wallet/monero/monero.dart';
import 'package:cw_core/cake_hive.dart'; import 'package:cw_core/cake_hive.dart';
import 'package:cake_wallet/core/flatpak.dart';
final navigatorKey = GlobalKey<NavigatorState>(); final navigatorKey = GlobalKey<NavigatorState>();
final rootKey = GlobalKey<RootState>(); final rootKey = GlobalKey<RootState>();
@ -74,7 +73,7 @@ Future<void> main() async {
Future<void> initializeAppConfigs() async { Future<void> initializeAppConfigs() async {
setRootDirFromEnv(); setRootDirFromEnv();
final appDir = await getAppDir(); final appDir = await getAppDir(isFlatpak: isFlatpak);
await CakeHive.close(); await CakeHive.close();
CakeHive.init(appDir.path); CakeHive.init(appDir.path);

View file

@ -224,13 +224,13 @@ class CWMonero extends Monero {
name: name, password: password, height: height, mnemonic: mnemonic); name: name, password: password, height: height, mnemonic: mnemonic);
@override @override
WalletCredentials createMoneroNewWalletCredentials({ WalletCredentials createMoneroNewWalletCredentials(
required String name, {required String name,
required String language, required String language,
required bool isPolyseed, required bool isPolyseed,
String? password}) => String? password}) =>
MoneroNewWalletCredentials( MoneroNewWalletCredentials(
name: name, password: password, language: language, isPolyseed: isPolyseed); name: name, password: password, language: language, isPolyseed: isPolyseed);
@override @override
Map<String, String> getKeys(Object wallet) { Map<String, String> getKeys(Object wallet) {
@ -302,9 +302,9 @@ class CWMonero extends Monero {
} }
@override @override
WalletService createMoneroWalletService( WalletService createMoneroWalletService(Box<WalletInfo> walletInfoSource,
Box<WalletInfo> walletInfoSource, Box<UnspentCoinsInfo> unspentCoinSource) => Box<UnspentCoinsInfo> unspentCoinSource, bool isFlatpak) =>
MoneroWalletService(walletInfoSource, unspentCoinSource); MoneroWalletService(walletInfoSource, unspentCoinSource, isFlatpak);
@override @override
String getTransactionAddress(Object wallet, int accountIndex, int addressIndex) { String getTransactionAddress(Object wallet, int accountIndex, int addressIndex) {

View file

@ -75,8 +75,9 @@ class CWNano extends Nano {
} }
@override @override
WalletService createNanoWalletService(Box<WalletInfo> walletInfoSource, bool isDirect) { WalletService createNanoWalletService(
return NanoWalletService(walletInfoSource, isDirect); Box<WalletInfo> walletInfoSource, bool isDirect, bool isFlatpak) {
return NanoWalletService(walletInfoSource, isDirect, isFlatpak);
} }
@override @override
@ -189,7 +190,6 @@ class CWNano extends Nano {
} }
class CWNanoUtil extends NanoUtil { class CWNanoUtil extends NanoUtil {
@override @override
bool isValidBip39Seed(String seed) { bool isValidBip39Seed(String seed) {
return NanoDerivations.isValidBip39Seed(seed); return NanoDerivations.isValidBip39Seed(seed);

View file

@ -4,15 +4,13 @@ class CWPolygon extends Polygon {
@override @override
List<String> getPolygonWordList(String language) => EthereumMnemonics.englishWordlist; List<String> getPolygonWordList(String language) => EthereumMnemonics.englishWordlist;
WalletService createPolygonWalletService(Box<WalletInfo> walletInfoSource, bool isDirect) => WalletService createPolygonWalletService(
PolygonWalletService(walletInfoSource); Box<WalletInfo> walletInfoSource, bool isDirec, bool isFlatpak) =>
PolygonWalletService(walletInfoSource, isFlatpak);
@override @override
WalletCredentials createPolygonNewWalletCredentials({ WalletCredentials createPolygonNewWalletCredentials(
required String name, {required String name, WalletInfo? walletInfo, String? password}) =>
WalletInfo? walletInfo,
String? password
}) =>
PolygonNewWalletCredentials(name: name, walletInfo: walletInfo, password: password); PolygonNewWalletCredentials(name: name, walletInfo: walletInfo, password: password);
@override @override

View file

@ -11,7 +11,7 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_mailer/flutter_mailer.dart'; import 'package:flutter_mailer/flutter_mailer.dart';
import 'package:package_info_plus/package_info_plus.dart'; import 'package:package_info_plus/package_info_plus.dart';
import 'package:path_provider/path_provider.dart'; import 'package:cake_wallet/core/flatpak.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
class ExceptionHandler { class ExceptionHandler {
@ -19,7 +19,7 @@ class ExceptionHandler {
static const _coolDownDurationInDays = 7; static const _coolDownDurationInDays = 7;
static void _saveException(String? error, StackTrace? stackTrace, {String? library}) async { static void _saveException(String? error, StackTrace? stackTrace, {String? library}) async {
final appDocDir = await getAppDir(); final appDocDir = await getAppDir(isFlatpak: isFlatpak);
final file = File('${appDocDir.path}/error.txt'); final file = File('${appDocDir.path}/error.txt');
final exception = { final exception = {
@ -49,7 +49,7 @@ class ExceptionHandler {
static void _sendExceptionFile() async { static void _sendExceptionFile() async {
try { try {
final appDocDir = await getAppDir(); final appDocDir = await getAppDir(isFlatpak: isFlatpak);
final file = File('${appDocDir.path}/error.txt'); final file = File('${appDocDir.path}/error.txt');

View file

@ -10,6 +10,7 @@ import 'package:mobx/mobx.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:cake_wallet/wallet_type_utils.dart'; import 'package:cake_wallet/wallet_type_utils.dart';
import 'package:path_provider/path_provider.dart'; import 'package:path_provider/path_provider.dart';
import 'package:cake_wallet/core/flatpak.dart';
part 'backup_view_model.g.dart'; part 'backup_view_model.g.dart';
@ -74,7 +75,7 @@ abstract class BackupViewModelBase with Store {
} }
Future<String> saveBackupFileLocally(BackupExportFile backup) async { Future<String> saveBackupFileLocally(BackupExportFile backup) async {
final appDir = await getAppDir(); final appDir = await getAppDir(isFlatpak: isFlatpak);
final path = '${appDir.path}/${backup.name}'; final path = '${appDir.path}/${backup.name}';
final backupFile = File(path); final backupFile = File(path);
await backupFile.writeAsBytes(backup.content); await backupFile.writeAsBytes(backup.content);
@ -82,7 +83,7 @@ abstract class BackupViewModelBase with Store {
} }
Future<void> removeBackupFileLocally(BackupExportFile backup) async { Future<void> removeBackupFileLocally(BackupExportFile backup) async {
final appDir = await getAppDir(); final appDir = await getAppDir(isFlatpak: isFlatpak);
final path = '${appDir.path}/${backup.name}'; final path = '${appDir.path}/${backup.name}';
final backupFile = File(path); final backupFile = File(path);
await backupFile.delete(); await backupFile.delete();

View file

@ -1,6 +1,7 @@
import 'dart:convert'; import 'dart:convert';
import 'package:cake_wallet/buy/buy_provider.dart'; import 'package:cake_wallet/buy/buy_provider.dart';
import 'package:cake_wallet/core/flatpak.dart';
import 'package:cake_wallet/core/key_service.dart'; import 'package:cake_wallet/core/key_service.dart';
import 'package:cake_wallet/entities/auto_generate_subaddress_status.dart'; import 'package:cake_wallet/entities/auto_generate_subaddress_status.dart';
import 'package:cake_wallet/entities/balance_display_mode.dart'; import 'package:cake_wallet/entities/balance_display_mode.dart';
@ -335,15 +336,13 @@ abstract class DashboardViewModelBase with Store {
bool hasExchangeAction; bool hasExchangeAction;
@computed @computed
bool get isEnabledBuyAction => bool get isEnabledBuyAction => !settingsStore.disableBuy && availableBuyProviders.isNotEmpty;
!settingsStore.disableBuy && availableBuyProviders.isNotEmpty;
@observable @observable
bool hasBuyAction; bool hasBuyAction;
@computed @computed
bool get isEnabledSellAction => bool get isEnabledSellAction => !settingsStore.disableSell && availableSellProviders.isNotEmpty;
!settingsStore.disableSell && availableSellProviders.isNotEmpty;
@observable @observable
bool hasSellAction; bool hasSellAction;
@ -467,8 +466,13 @@ abstract class DashboardViewModelBase with Store {
void setSyncAll(bool value) => settingsStore.currentSyncAll = value; void setSyncAll(bool value) => settingsStore.currentSyncAll = value;
Future<List<String>> checkAffectedWallets() async { Future<List<String>> checkAffectedWallets() async {
if (SettingsStoreBase.walletPasswordDirectInput) {
return [];
}
// await load file // await load file
final vulnerableSeedsString = await rootBundle.loadString('assets/text/cakewallet_weak_bitcoin_seeds_hashed_sorted_version1.txt'); final vulnerableSeedsString = await rootBundle
.loadString('assets/text/cakewallet_weak_bitcoin_seeds_hashed_sorted_version1.txt');
final vulnerableSeeds = vulnerableSeedsString.split("\n"); final vulnerableSeeds = vulnerableSeedsString.split("\n");
final walletInfoSource = await CakeHive.openBox<WalletInfo>(WalletInfo.boxName); final walletInfoSource = await CakeHive.openBox<WalletInfo>(WalletInfo.boxName);
@ -477,7 +481,8 @@ abstract class DashboardViewModelBase with Store {
for (var walletInfo in walletInfoSource.values) { for (var walletInfo in walletInfoSource.values) {
if (walletInfo.type == WalletType.bitcoin) { if (walletInfo.type == WalletType.bitcoin) {
final password = await keyService.getWalletPassword(walletName: walletInfo.name); final password = await keyService.getWalletPassword(walletName: walletInfo.name);
final path = await pathForWallet(name: walletInfo.name, type: walletInfo.type); final path =
await pathForWallet(name: walletInfo.name, type: walletInfo.type, isFlatpak: isFlatpak);
final jsonSource = await read(path: path, password: password); final jsonSource = await read(path: path, password: password);
final data = json.decode(jsonSource) as Map; final data = json.decode(jsonSource) as Map;
final mnemonic = data['mnemonic'] as String; final mnemonic = data['mnemonic'] as String;

View file

@ -1,3 +1,4 @@
import 'package:cake_wallet/core/flatpak.dart';
import 'package:cake_wallet/core/wallet_creation_service.dart'; import 'package:cake_wallet/core/wallet_creation_service.dart';
import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/store/settings_store.dart'; import 'package:cake_wallet/store/settings_store.dart';
@ -66,8 +67,8 @@ abstract class WalletCreationVMBase with Store {
} }
walletCreationService.checkIfExists(name); walletCreationService.checkIfExists(name);
final dirPath = await pathForWalletDir(name: name, type: type); final dirPath = await pathForWalletDir(name: name, type: type, isFlatpak: isFlatpak);
final path = await pathForWallet(name: name, type: type); final path = await pathForWallet(name: name, type: type, isFlatpak: isFlatpak);
final credentials = restoreWallet != null final credentials = restoreWallet != null
? getCredentialsFromRestoredWallet(options, restoreWallet) ? getCredentialsFromRestoredWallet(options, restoreWallet)
: getCredentials(options); : getCredentials(options);

View file

@ -1,6 +1,7 @@
#!/bin/bash #!/bin/bash
CAKEWALLET="cakewallet" CAKEWALLET="cakewallet"
CAKEWALLET_FLATPAK="cakewallet-flatpak"
DIR=`pwd` DIR=`pwd`
if [ -z "$APP_LINUX_TYPE" ]; then if [ -z "$APP_LINUX_TYPE" ]; then
@ -14,6 +15,8 @@ CONFIG_ARGS=""
case $APP_LINUX_TYPE in case $APP_LINUX_TYPE in
$CAKEWALLET) $CAKEWALLET)
CONFIG_ARGS="--monero --bitcoin --ethereum --polygon --nano --bitcoinCash --excludeFlutterSecureStorage";; CONFIG_ARGS="--monero --bitcoin --ethereum --polygon --nano --bitcoinCash --excludeFlutterSecureStorage";;
$CAKEWALLET_FLATPAK)
CONFIG_ARGS="--monero --bitcoin --ethereum --polygon --nano --bitcoinCash --excludeFlutterSecureStorage --flatpak";;
esac esac
cp -rf pubspec_description.yaml pubspec.yaml cp -rf pubspec_description.yaml pubspec.yaml

View file

@ -0,0 +1,33 @@
#!/bin/fish
set APP_LINUX_NAME ""
set APP_LINUX_VERSION ""
set APP_LINUX_BUILD_VERSION ""
set CAKEWALLET "cakewallet"
set CAKEWALLET_FLATPAK "cakewallet-flatpak"
set TYPES $CAKEWALLET $CAKEWALLET_FLATPAK
set APP_LINUX_TYPE $argv[1]
if not contains $APP_LINUX_TYPE $TYPES
echo "Wrong app type."
exit 1
end
set CAKEWALLET_NAME "Cake Wallet"
set CAKEWALLET_VERSION "1.4.0"
set CAKEWALLET_BUILD_NUMBER 13
switch $APP_LINUX_TYPE
case $CAKEWALLET
case $CAKEWALLET_FLATPAK
set APP_LINUX_NAME $CAKEWALLET_NAME
set APP_LINUX_VERSION $CAKEWALLET_VERSION
set APP_LINUX_BUILD_NUMBER $CAKEWALLET_BUILD_NUMBER
end
set -x APP_LINUX_TYPE $APP_LINUX_TYPE
set -x APP_LINUX_NAME $APP_LINUX_NAME
set -x APP_LINUX_VERSION $APP_LINUX_VERSION
set -x APP_LINUX_BUILD_NUMBER $APP_LINUX_BUILD_NUMBER

View file

@ -5,25 +5,25 @@ APP_LINUX_VERSION=""
APP_LINUX_BUILD_VERSION="" APP_LINUX_BUILD_VERSION=""
CAKEWALLET="cakewallet" CAKEWALLET="cakewallet"
CAKEWALLET_FLATPAK="cakewallet-flatpak"
TYPES=($CAKEWALLET) TYPES=($CAKEWALLET $CAKEWALLET_FLATPAK)
APP_LINUX_TYPE=$CAKEWALLET APP_LINUX_TYPE=$1
if [ -n "$1" ]; then if ! [[ " ${TYPES[*]} " =~ " ${APP_LINUX_TYPE} " ]]; then
APP_LINUX_TYPE=$1 echo ${TYPES[*]}
echo ${APP_LINUX_TYPE}
exit 1
fi fi
CAKEWALLET_NAME="Cake Wallet" CAKEWALLET_NAME="Cake Wallet"
CAKEWALLET_VERSION="1.4.1" CAKEWALLET_VERSION="1.4.1"
CAKEWALLET_BUILD_NUMBER=14 CAKEWALLET_BUILD_NUMBER=14
if ! [[ " ${TYPES[*]} " =~ " ${APP_LINUX_TYPE} " ]]; then
echo "Wrong app type."
exit 1
fi
case $APP_LINUX_TYPE in case $APP_LINUX_TYPE in
$CAKEWALLET) $CAKEWALLET)
;;
$CAKEWALLET_FLATPAK)
APP_LINUX_NAME=$CAKEWALLET_NAME APP_LINUX_NAME=$CAKEWALLET_NAME
APP_LINUX_VERSION=$CAKEWALLET_VERSION APP_LINUX_VERSION=$CAKEWALLET_VERSION
APP_LINUX_BUILD_NUMBER=$CAKEWALLET_BUILD_NUMBER;; APP_LINUX_BUILD_NUMBER=$CAKEWALLET_BUILD_NUMBER;;

View file

@ -9,6 +9,7 @@ const nanoOutputPath = 'lib/nano/nano.dart';
const polygonOutputPath = 'lib/polygon/polygon.dart'; const polygonOutputPath = 'lib/polygon/polygon.dart';
const walletTypesPath = 'lib/wallet_types.g.dart'; const walletTypesPath = 'lib/wallet_types.g.dart';
const secureStoragePath = 'lib/core/secure_storage.dart'; const secureStoragePath = 'lib/core/secure_storage.dart';
const flatpakPath = 'lib/core/flatpak.dart';
const pubspecDefaultPath = 'pubspec_default.yaml'; const pubspecDefaultPath = 'pubspec_default.yaml';
const pubspecOutputPath = 'pubspec.yaml'; const pubspecOutputPath = 'pubspec.yaml';
@ -24,6 +25,7 @@ Future<void> main(List<String> args) async {
final hasPolygon = args.contains('${prefix}polygon'); final hasPolygon = args.contains('${prefix}polygon');
final excludeFlutterSecureStorage = args.contains('${prefix}excludeFlutterSecureStorage'); final excludeFlutterSecureStorage = args.contains('${prefix}excludeFlutterSecureStorage');
final isFlatpak = args.contains('${prefix}flatpak');
await generateBitcoin(hasBitcoin); await generateBitcoin(hasBitcoin);
await generateMonero(hasMonero); await generateMonero(hasMonero);
await generateHaven(hasHaven); await generateHaven(hasHaven);
@ -55,6 +57,7 @@ Future<void> main(List<String> args) async {
hasPolygon: hasPolygon, hasPolygon: hasPolygon,
); );
await injectSecureStorage(!excludeFlutterSecureStorage); await injectSecureStorage(!excludeFlutterSecureStorage);
await injectFlatpak(isFlatpak);
} }
Future<void> generateBitcoin(bool hasImplementation) async { Future<void> generateBitcoin(bool hasImplementation) async {
@ -132,8 +135,8 @@ abstract class Bitcoin {
List<Unspent> getUnspents(Object wallet); List<Unspent> getUnspents(Object wallet);
void updateUnspents(Object wallet); void updateUnspents(Object wallet);
WalletService createBitcoinWalletService(Box<WalletInfo> walletInfoSource, Box<UnspentCoinsInfo> unspentCoinSource, bool isDirect); WalletService createBitcoinWalletService(Box<WalletInfo> walletInfoSource, Box<UnspentCoinsInfo> unspentCoinSource, bool isDirect, bool isFlatpak);
WalletService createLitecoinWalletService(Box<WalletInfo> walletInfoSource, Box<UnspentCoinsInfo> unspentCoinSource, bool isDirect); WalletService createLitecoinWalletService(Box<WalletInfo> walletInfoSource, Box<UnspentCoinsInfo> unspentCoinSource, bool isDirect, bool isFlatpak);
TransactionPriority getBitcoinTransactionPriorityMedium(); TransactionPriority getBitcoinTransactionPriorityMedium();
TransactionPriority getLitecoinTransactionPriorityMedium(); TransactionPriority getLitecoinTransactionPriorityMedium();
TransactionPriority getBitcoinTransactionPrioritySlow(); TransactionPriority getBitcoinTransactionPrioritySlow();
@ -296,7 +299,7 @@ abstract class Monero {
void setCurrentAccount(Object wallet, int id, String label, String? balance); void setCurrentAccount(Object wallet, int id, String label, String? balance);
void onStartup(); void onStartup();
int getTransactionInfoAccountId(TransactionInfo tx); int getTransactionInfoAccountId(TransactionInfo tx);
WalletService createMoneroWalletService(Box<WalletInfo> walletInfoSource, Box<UnspentCoinsInfo> unspentCoinSource); WalletService createMoneroWalletService(Box<WalletInfo> walletInfoSource, Box<UnspentCoinsInfo> unspentCoinSource, bool isFlatpak);
Map<String, String> pendingTransactionInfo(Object transaction); Map<String, String> pendingTransactionInfo(Object transaction);
} }
@ -547,7 +550,7 @@ import 'package:cw_ethereum/ethereum_transaction_priority.dart';
const ethereumContent = """ const ethereumContent = """
abstract class Ethereum { abstract class Ethereum {
List<String> getEthereumWordList(String language); List<String> getEthereumWordList(String language);
WalletService createEthereumWalletService(Box<WalletInfo> walletInfoSource, bool isDirect); WalletService createEthereumWalletService(Box<WalletInfo> walletInfoSource, bool isDirect, bool isFlatpak);
WalletCredentials createEthereumNewWalletCredentials({required String name, WalletInfo? walletInfo, String? password}); WalletCredentials createEthereumNewWalletCredentials({required String name, WalletInfo? walletInfo, String? password});
WalletCredentials createEthereumRestoreWalletFromSeedCredentials({required String name, required String mnemonic, required String password}); WalletCredentials createEthereumRestoreWalletFromSeedCredentials({required String name, required String mnemonic, required String password});
WalletCredentials createEthereumRestoreWalletFromPrivateKey({required String name, required String privateKey, required String password}); WalletCredentials createEthereumRestoreWalletFromPrivateKey({required String name, required String privateKey, required String password});
@ -634,7 +637,7 @@ import 'package:cw_ethereum/ethereum_mnemonics.dart';
const polygonContent = """ const polygonContent = """
abstract class Polygon { abstract class Polygon {
List<String> getPolygonWordList(String language); List<String> getPolygonWordList(String language);
WalletService createPolygonWalletService(Box<WalletInfo> walletInfoSource, bool isDirect); WalletService createPolygonWalletService(Box<WalletInfo> walletInfoSource, bool isDirec, bool isFlatpak);
WalletCredentials createPolygonNewWalletCredentials({required String name, WalletInfo? walletInfo, String? password}); WalletCredentials createPolygonNewWalletCredentials({required String name, WalletInfo? walletInfo, String? password});
WalletCredentials createPolygonRestoreWalletFromSeedCredentials({required String name, required String mnemonic, required String password}); WalletCredentials createPolygonRestoreWalletFromSeedCredentials({required String name, required String mnemonic, required String password});
WalletCredentials createPolygonRestoreWalletFromPrivateKey({required String name, required String privateKey, required String password}); WalletCredentials createPolygonRestoreWalletFromPrivateKey({required String name, required String privateKey, required String password});
@ -717,7 +720,7 @@ abstract class BitcoinCash {
String getCashAddrFormat(String address); String getCashAddrFormat(String address);
WalletService createBitcoinCashWalletService( WalletService createBitcoinCashWalletService(
Box<WalletInfo> walletInfoSource, Box<UnspentCoinsInfo> unspentCoinSource, bool isDirect); Box<WalletInfo> walletInfoSource, Box<UnspentCoinsInfo> unspentCoinSource, bool isDirect, bool isFlatpak);
WalletCredentials createBitcoinCashNewWalletCredentials( WalletCredentials createBitcoinCashNewWalletCredentials(
{required String name, WalletInfo? walletInfo, String? password}); {required String name, WalletInfo? walletInfo, String? password});
@ -796,7 +799,7 @@ abstract class Nano {
void setCurrentAccount(Object wallet, int id, String label, String? balance); void setCurrentAccount(Object wallet, int id, String label, String? balance);
WalletService createNanoWalletService(Box<WalletInfo> walletInfoSource, bool isDirect); WalletService createNanoWalletService(Box<WalletInfo> walletInfoSource, bool isDirec, bool isFlatpak);
WalletCredentials createNanoNewWalletCredentials({ WalletCredentials createNanoNewWalletCredentials({
required String name, required String name,
@ -1054,7 +1057,8 @@ Future<void> generateWalletTypes(
} }
Future<void> injectSecureStorage(bool hasFlutterSecureStorage) async { Future<void> injectSecureStorage(bool hasFlutterSecureStorage) async {
const flutterSecureStorageHeader = "import 'package:flutter_secure_storage/flutter_secure_storage.dart';"; const flutterSecureStorageHeader =
"import 'package:flutter_secure_storage/flutter_secure_storage.dart';";
const abstractSecureStorage = """ const abstractSecureStorage = """
abstract class SecureStorage { abstract class SecureStorage {
Future<String?> read({required String key}); Future<String?> read({required String key});
@ -1105,8 +1109,8 @@ class FakeSecureStorage extends SecureStorage {
}"""; }""";
final outputFile = File(secureStoragePath); final outputFile = File(secureStoragePath);
final header = hasFlutterSecureStorage final header = hasFlutterSecureStorage
? '${flutterSecureStorageHeader}\n\nfinal SecureStorage secureStorageShared = DefaultSecureStorage();\n' ? '${flutterSecureStorageHeader}\n\nfinal SecureStorage secureStorageShared = DefaultSecureStorage();\n'
: 'final SecureStorage secureStorageShared = FakeSecureStorage();\n'; : 'final SecureStorage secureStorageShared = FakeSecureStorage();\n';
var output = ''; var output = '';
if (outputFile.existsSync()) { if (outputFile.existsSync()) {
await outputFile.delete(); await outputFile.delete();
@ -1122,3 +1126,13 @@ class FakeSecureStorage extends SecureStorage {
await outputFile.writeAsString(output); await outputFile.writeAsString(output);
} }
Future<void> injectFlatpak(bool isFlatpak) async {
final outputFile = File(flatpakPath);
if (outputFile.existsSync()) {
await outputFile.delete();
}
await outputFile
.writeAsString(isFlatpak ? "const isFlatpak = true;" : "const isFlatpak = false;");
}