mirror of
https://github.com/cake-tech/cake_wallet.git
synced 2024-12-22 11:39:22 +00:00
Merge branch 'main' of https://github.com/cake-tech/cake_wallet into CW-434-Prompt-to-update-app
This commit is contained in:
commit
0d295cd359
151 changed files with 3258 additions and 1698 deletions
3
.github/workflows/cache_dependencies.yml
vendored
3
.github/workflows/cache_dependencies.yml
vendored
|
@ -1,6 +1,7 @@
|
|||
name: Cache Dependencies
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches: [ main ]
|
||||
|
||||
|
@ -45,7 +46,7 @@ jobs:
|
|||
/opt/android/cake_wallet/cw_monero/android/.cxx
|
||||
/opt/android/cake_wallet/cw_monero/ios/External
|
||||
/opt/android/cake_wallet/cw_shared_external/ios/External
|
||||
key: ${{ hashFiles('**/build_monero.sh', '**/build_haven.sh') }}
|
||||
key: ${{ hashFiles('**/build_monero.sh', '**/build_haven.sh', '**/monero_api.cpp') }}
|
||||
|
||||
- if: ${{ steps.cache-externals.outputs.cache-hit != 'true' }}
|
||||
name: Generate Externals
|
||||
|
|
14
.github/workflows/pr_test_build.yml
vendored
14
.github/workflows/pr_test_build.yml
vendored
|
@ -55,7 +55,7 @@ jobs:
|
|||
/opt/android/cake_wallet/cw_monero/android/.cxx
|
||||
/opt/android/cake_wallet/cw_monero/ios/External
|
||||
/opt/android/cake_wallet/cw_shared_external/ios/External
|
||||
key: ${{ hashFiles('**/build_monero.sh', '**/build_haven.sh') }}
|
||||
key: ${{ hashFiles('**/build_monero.sh', '**/build_haven.sh', '**/monero_api.cpp') }}
|
||||
|
||||
- if: ${{ steps.cache-externals.outputs.cache-hit != 'true' }}
|
||||
name: Generate Externals
|
||||
|
@ -163,7 +163,11 @@ jobs:
|
|||
|
||||
- name: Send Test APK
|
||||
continue-on-error: true
|
||||
run: |
|
||||
cd /opt/android/cake_wallet
|
||||
var=$(curl --upload-file build/app/outputs/apk/release/app-release.apk https://transfer.sh/$GITHUB_HEAD_REF.apk -H "Max-Days: 10")
|
||||
curl ${{ secrets.SLACK_WEB_HOOK }} -H "Content-Type: application/json" -d '{"apk_link": "'"$var"'","ticket": "'"$GITHUB_HEAD_REF"'"}'
|
||||
uses: adrey/slack-file-upload-action@1.0.5
|
||||
with:
|
||||
token: ${{ secrets.SLACK_APP_TOKEN }}
|
||||
path: /opt/android/cake_wallet/build/app/outputs/apk/release/app-release.apk
|
||||
channel: ${{ secrets.SLACK_APK_CHANNEL }}
|
||||
title: '${{github.head_ref}}.apk'
|
||||
filename: ${{github.head_ref}}.apk
|
||||
initial_comment: ${{ github.event.head_commit.message }}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
class BitcoinTransactionNoInputsException implements Exception {
|
||||
@override
|
||||
String toString() => 'Not enough inputs available';
|
||||
String toString() => 'Not enough inputs available. Please select more under Coin Control';
|
||||
}
|
||||
|
|
21
cw_core/lib/address_info.dart
Normal file
21
cw_core/lib/address_info.dart
Normal file
|
@ -0,0 +1,21 @@
|
|||
import 'package:cw_core/hive_type_ids.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
|
||||
part 'address_info.g.dart';
|
||||
|
||||
@HiveType(typeId: ADDRESS_INFO_TYPE_ID)
|
||||
class AddressInfo extends HiveObject {
|
||||
AddressInfo({required this.address, this.accountIndex, required this.label});
|
||||
|
||||
static const typeId = ADDRESS_INFO_TYPE_ID;
|
||||
static const boxName = 'AddressInfo';
|
||||
|
||||
@HiveField(0)
|
||||
int? accountIndex;
|
||||
|
||||
@HiveField(1, defaultValue: '')
|
||||
String address;
|
||||
|
||||
@HiveField(2, defaultValue: '')
|
||||
String label;
|
||||
}
|
|
@ -9,5 +9,5 @@ const EXCHANGE_TEMPLATE_TYPE_ID = 7;
|
|||
const ORDER_TYPE_ID = 8;
|
||||
const UNSPENT_COINS_INFO_TYPE_ID = 9;
|
||||
const ANONPAY_INVOICE_INFO_TYPE_ID = 10;
|
||||
|
||||
const ADDRESS_INFO_TYPE_ID = 11;
|
||||
const ERC20_TOKEN_TYPE_ID = 12;
|
||||
|
|
|
@ -2,24 +2,31 @@ import 'package:cw_core/balance.dart';
|
|||
import 'package:cw_core/monero_amount_format.dart';
|
||||
|
||||
class MoneroBalance extends Balance {
|
||||
MoneroBalance({required this.fullBalance, required this.unlockedBalance})
|
||||
MoneroBalance({required this.fullBalance, required this.unlockedBalance, this.frozenBalance = 0})
|
||||
: formattedFullBalance = moneroAmountToString(amount: fullBalance),
|
||||
formattedUnlockedBalance =
|
||||
moneroAmountToString(amount: unlockedBalance),
|
||||
formattedUnlockedBalance = moneroAmountToString(amount: unlockedBalance),
|
||||
frozenFormatted = moneroAmountToString(amount: frozenBalance),
|
||||
super(unlockedBalance, fullBalance);
|
||||
|
||||
MoneroBalance.fromString(
|
||||
{required this.formattedFullBalance,
|
||||
required this.formattedUnlockedBalance})
|
||||
required this.formattedUnlockedBalance,
|
||||
this.frozenFormatted = '0.0'})
|
||||
: fullBalance = moneroParseAmount(amount: formattedFullBalance),
|
||||
unlockedBalance = moneroParseAmount(amount: formattedUnlockedBalance),
|
||||
frozenBalance = moneroParseAmount(amount: frozenFormatted),
|
||||
super(moneroParseAmount(amount: formattedUnlockedBalance),
|
||||
moneroParseAmount(amount: formattedFullBalance));
|
||||
|
||||
final int fullBalance;
|
||||
final int unlockedBalance;
|
||||
final int frozenBalance;
|
||||
final String formattedFullBalance;
|
||||
final String formattedUnlockedBalance;
|
||||
final String frozenFormatted;
|
||||
|
||||
@override
|
||||
String get formattedFrozenBalance => frozenFormatted == '0.0' ? '' : frozenFormatted;
|
||||
|
||||
@override
|
||||
String get formattedAvailableBalance => formattedUnlockedBalance;
|
||||
|
|
|
@ -13,7 +13,9 @@ class UnspentCoinsInfo extends HiveObject {
|
|||
required this.noteRaw,
|
||||
required this.address,
|
||||
required this.vout,
|
||||
required this.value});
|
||||
required this.value,
|
||||
this.keyImage = null
|
||||
});
|
||||
|
||||
static const typeId = UNSPENT_COINS_INFO_TYPE_ID;
|
||||
static const boxName = 'Unspent';
|
||||
|
@ -43,6 +45,9 @@ class UnspentCoinsInfo extends HiveObject {
|
|||
@HiveField(7, defaultValue: 0)
|
||||
int vout;
|
||||
|
||||
@HiveField(8, defaultValue: null)
|
||||
String? keyImage;
|
||||
|
||||
String get note => noteRaw ?? '';
|
||||
|
||||
set note(String value) => noteRaw = value;
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
import 'package:cw_core/address_info.dart';
|
||||
import 'package:cw_core/wallet_info.dart';
|
||||
|
||||
abstract class WalletAddresses {
|
||||
WalletAddresses(this.walletInfo)
|
||||
: addressesMap = {};
|
||||
: addressesMap = {},
|
||||
addressInfos = {};
|
||||
|
||||
final WalletInfo walletInfo;
|
||||
|
||||
|
@ -12,6 +14,10 @@ abstract class WalletAddresses {
|
|||
|
||||
Map<String, String> addressesMap;
|
||||
|
||||
Map<int, List<AddressInfo>> addressInfos;
|
||||
|
||||
Set<String> usedAddresses = {};
|
||||
|
||||
Future<void> init();
|
||||
|
||||
Future<void> updateAddressesInBox();
|
||||
|
@ -20,6 +26,8 @@ abstract class WalletAddresses {
|
|||
try {
|
||||
walletInfo.address = address;
|
||||
walletInfo.addresses = addressesMap;
|
||||
walletInfo.addressInfos = addressInfos;
|
||||
walletInfo.usedAddresses = usedAddresses.toList();
|
||||
|
||||
if (walletInfo.isInBox) {
|
||||
await walletInfo.save();
|
||||
|
|
|
@ -42,7 +42,9 @@ abstract class WalletBase<
|
|||
|
||||
set syncStatus(SyncStatus status);
|
||||
|
||||
String get seed;
|
||||
String? get seed;
|
||||
|
||||
String? get privateKey => null;
|
||||
|
||||
Object get keys;
|
||||
|
||||
|
@ -50,6 +52,10 @@ abstract class WalletBase<
|
|||
|
||||
late HistoryType transactionHistory;
|
||||
|
||||
set isEnabledAutoGenerateSubaddress(bool value) {}
|
||||
|
||||
bool get isEnabledAutoGenerateSubaddress => false;
|
||||
|
||||
Future<void> connectToNode({required Node node});
|
||||
|
||||
Future<void> startSync();
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import 'dart:async';
|
||||
import 'package:cw_core/address_info.dart';
|
||||
import 'package:cw_core/hive_type_ids.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
|
@ -72,6 +73,12 @@ class WalletInfo extends HiveObject {
|
|||
@HiveField(13)
|
||||
bool? showIntroCakePayCard;
|
||||
|
||||
@HiveField(14)
|
||||
Map<int, List<AddressInfo>>? addressInfos;
|
||||
|
||||
@HiveField(15)
|
||||
List<String>? usedAddresses;
|
||||
|
||||
String get yatLastUsedAddress => yatLastUsedAddressRaw ?? '';
|
||||
|
||||
set yatLastUsedAddress(String address) {
|
||||
|
|
|
@ -44,12 +44,14 @@ abstract class EthereumWalletBase
|
|||
with Store {
|
||||
EthereumWalletBase({
|
||||
required WalletInfo walletInfo,
|
||||
required String mnemonic,
|
||||
String? mnemonic,
|
||||
String? privateKey,
|
||||
required String password,
|
||||
ERC20Balance? initialBalance,
|
||||
}) : syncStatus = NotConnectedSyncStatus(),
|
||||
_password = password,
|
||||
_mnemonic = mnemonic,
|
||||
_hexPrivateKey = privateKey,
|
||||
_isTransactionUpdating = false,
|
||||
_client = EthereumClient(),
|
||||
walletAddresses = EthereumWalletAddresses(walletInfo),
|
||||
|
@ -66,12 +68,13 @@ abstract class EthereumWalletBase
|
|||
_sharedPrefs.complete(SharedPreferences.getInstance());
|
||||
}
|
||||
|
||||
final String _mnemonic;
|
||||
final String? _mnemonic;
|
||||
final String? _hexPrivateKey;
|
||||
final String _password;
|
||||
|
||||
late final Box<Erc20Token> erc20TokensBox;
|
||||
|
||||
late final EthPrivateKey _privateKey;
|
||||
late final EthPrivateKey _ethPrivateKey;
|
||||
|
||||
late EthereumClient _client;
|
||||
|
||||
|
@ -99,8 +102,12 @@ abstract class EthereumWalletBase
|
|||
erc20TokensBox = await CakeHive.openBox<Erc20Token>(Erc20Token.boxName);
|
||||
await walletAddresses.init();
|
||||
await transactionHistory.init();
|
||||
_privateKey = await getPrivateKey(_mnemonic, _password);
|
||||
walletAddresses.address = _privateKey.address.toString();
|
||||
_ethPrivateKey = await getPrivateKey(
|
||||
mnemonic: _mnemonic,
|
||||
privateKey: _hexPrivateKey,
|
||||
password: _password,
|
||||
);
|
||||
walletAddresses.address = _ethPrivateKey.address.toString();
|
||||
await save();
|
||||
}
|
||||
|
||||
|
@ -108,8 +115,7 @@ abstract class EthereumWalletBase
|
|||
int calculateEstimatedFee(TransactionPriority priority, int? amount) {
|
||||
try {
|
||||
if (priority is EthereumTransactionPriority) {
|
||||
final priorityFee =
|
||||
EtherAmount.fromUnitAndValue(EtherUnit.gwei, priority.tip).getInWei.toInt();
|
||||
final priorityFee = EtherAmount.fromInt(EtherUnit.gwei, priority.tip).getInWei.toInt();
|
||||
return (_gasPrice! + priorityFee) * (_estimatedGas ?? 0);
|
||||
}
|
||||
|
||||
|
@ -142,7 +148,7 @@ abstract class EthereumWalletBase
|
|||
throw Exception("Ethereum Node connection failed");
|
||||
}
|
||||
|
||||
_client.setListeners(_privateKey.address, _onNewTransaction);
|
||||
_client.setListeners(_ethPrivateKey.address, _onNewTransaction);
|
||||
|
||||
_setTransactionUpdateTimer();
|
||||
|
||||
|
@ -202,7 +208,7 @@ abstract class EthereumWalletBase
|
|||
}
|
||||
|
||||
final pendingEthereumTransaction = await _client.signTransaction(
|
||||
privateKey: _privateKey,
|
||||
privateKey: _ethPrivateKey,
|
||||
toAddress: _credentials.outputs.first.isParsedAddress
|
||||
? _credentials.outputs.first.extractedAddress!
|
||||
: _credentials.outputs.first.address,
|
||||
|
@ -240,7 +246,7 @@ abstract class EthereumWalletBase
|
|||
|
||||
@override
|
||||
Future<Map<String, EthereumTransactionInfo>> fetchTransactions() async {
|
||||
final address = _privateKey.address.hex;
|
||||
final address = _ethPrivateKey.address.hex;
|
||||
final transactions = await _client.fetchTransactions(address);
|
||||
|
||||
final List<Future<List<EthereumTransactionModel>>> erc20TokensTransactions = [];
|
||||
|
@ -300,7 +306,10 @@ abstract class EthereumWalletBase
|
|||
}
|
||||
|
||||
@override
|
||||
String get seed => _mnemonic;
|
||||
String? get seed => _mnemonic;
|
||||
|
||||
@override
|
||||
String get privateKey => HEX.encode(_ethPrivateKey.privateKey);
|
||||
|
||||
@action
|
||||
@override
|
||||
|
@ -327,6 +336,7 @@ abstract class EthereumWalletBase
|
|||
|
||||
String toJSON() => json.encode({
|
||||
'mnemonic': _mnemonic,
|
||||
'private_key': privateKey,
|
||||
'balance': balance[currency]!.toJSON(),
|
||||
});
|
||||
|
||||
|
@ -338,13 +348,15 @@ abstract class EthereumWalletBase
|
|||
final path = await pathForWallet(name: name, type: walletInfo.type);
|
||||
final jsonSource = await read(path: path, password: password);
|
||||
final data = json.decode(jsonSource) as Map;
|
||||
final mnemonic = data['mnemonic'] as String;
|
||||
final mnemonic = data['mnemonic'] as String?;
|
||||
final privateKey = data['private_key'] as String?;
|
||||
final balance = ERC20Balance.fromJSON(data['balance'] as String) ?? ERC20Balance(BigInt.zero);
|
||||
|
||||
return EthereumWallet(
|
||||
walletInfo: walletInfo,
|
||||
password: password,
|
||||
mnemonic: mnemonic,
|
||||
privateKey: privateKey,
|
||||
initialBalance: balance,
|
||||
);
|
||||
}
|
||||
|
@ -357,7 +369,7 @@ abstract class EthereumWalletBase
|
|||
}
|
||||
|
||||
Future<ERC20Balance> _fetchEthBalance() async {
|
||||
final balance = await _client.getBalance(_privateKey.address);
|
||||
final balance = await _client.getBalance(_ethPrivateKey.address);
|
||||
return ERC20Balance(balance.getInWei);
|
||||
}
|
||||
|
||||
|
@ -366,7 +378,7 @@ abstract class EthereumWalletBase
|
|||
try {
|
||||
if (token.enabled) {
|
||||
balance[token] = await _client.fetchERC20Balances(
|
||||
_privateKey.address,
|
||||
_ethPrivateKey.address,
|
||||
token.contractAddress,
|
||||
);
|
||||
} else {
|
||||
|
@ -376,8 +388,15 @@ abstract class EthereumWalletBase
|
|||
}
|
||||
}
|
||||
|
||||
Future<EthPrivateKey> getPrivateKey(String mnemonic, String password) async {
|
||||
final seed = bip39.mnemonicToSeed(mnemonic);
|
||||
Future<EthPrivateKey> getPrivateKey(
|
||||
{String? mnemonic, String? privateKey, required String password}) async {
|
||||
assert(mnemonic != null || privateKey != null);
|
||||
|
||||
if (privateKey != null) {
|
||||
return EthPrivateKey.fromHex(privateKey);
|
||||
}
|
||||
|
||||
final seed = bip39.mnemonicToSeed(mnemonic!);
|
||||
|
||||
final root = bip32.BIP32.fromSeed(seed);
|
||||
|
||||
|
@ -413,7 +432,7 @@ abstract class EthereumWalletBase
|
|||
|
||||
if (_token.enabled) {
|
||||
balance[_token] = await _client.fetchERC20Balances(
|
||||
_privateKey.address,
|
||||
_ethPrivateKey.address,
|
||||
_token.contractAddress,
|
||||
);
|
||||
} else {
|
||||
|
|
|
@ -8,16 +8,22 @@ class EthereumNewWalletCredentials extends WalletCredentials {
|
|||
|
||||
class EthereumRestoreWalletFromSeedCredentials extends WalletCredentials {
|
||||
EthereumRestoreWalletFromSeedCredentials(
|
||||
{required String name, required String password, required this.mnemonic, WalletInfo? walletInfo})
|
||||
{required String name,
|
||||
required String password,
|
||||
required this.mnemonic,
|
||||
WalletInfo? walletInfo})
|
||||
: super(name: name, password: password, walletInfo: walletInfo);
|
||||
|
||||
final String mnemonic;
|
||||
}
|
||||
|
||||
class EthereumRestoreWalletFromWIFCredentials extends WalletCredentials {
|
||||
EthereumRestoreWalletFromWIFCredentials(
|
||||
{required String name, required String password, required this.wif, WalletInfo? walletInfo})
|
||||
class EthereumRestoreWalletFromPrivateKey extends WalletCredentials {
|
||||
EthereumRestoreWalletFromPrivateKey(
|
||||
{required String name,
|
||||
required String password,
|
||||
required this.privateKey,
|
||||
WalletInfo? walletInfo})
|
||||
: super(name: name, password: password, walletInfo: walletInfo);
|
||||
|
||||
final String wif;
|
||||
final String privateKey;
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ import 'package:bip39/bip39.dart' as bip39;
|
|||
import 'package:collection/collection.dart';
|
||||
|
||||
class EthereumWalletService extends WalletService<EthereumNewWalletCredentials,
|
||||
EthereumRestoreWalletFromSeedCredentials, EthereumRestoreWalletFromWIFCredentials> {
|
||||
EthereumRestoreWalletFromSeedCredentials, EthereumRestoreWalletFromPrivateKey> {
|
||||
EthereumWalletService(this.walletInfoSource);
|
||||
|
||||
final Box<WalletInfo> walletInfoSource;
|
||||
|
@ -66,8 +66,18 @@ class EthereumWalletService extends WalletService<EthereumNewWalletCredentials,
|
|||
}
|
||||
|
||||
@override
|
||||
Future<EthereumWallet> restoreFromKeys(credentials) {
|
||||
throw UnimplementedError();
|
||||
Future<EthereumWallet> restoreFromKeys(EthereumRestoreWalletFromPrivateKey credentials) async {
|
||||
final wallet = EthereumWallet(
|
||||
password: credentials.password!,
|
||||
privateKey: credentials.privateKey,
|
||||
walletInfo: credentials.walletInfo!,
|
||||
);
|
||||
|
||||
await wallet.init();
|
||||
wallet.addInitialTokens();
|
||||
await wallet.save();
|
||||
|
||||
return wallet;
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
|
@ -12,8 +12,7 @@ import 'package:cw_core/monero_wallet_utils.dart';
|
|||
import 'package:cw_haven/api/structs/pending_transaction.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:cw_haven/api/transaction_history.dart'
|
||||
as haven_transaction_history;
|
||||
import 'package:cw_haven/api/transaction_history.dart' as haven_transaction_history;
|
||||
//import 'package:cw_haven/wallet.dart';
|
||||
import 'package:cw_haven/api/wallet.dart' as haven_wallet;
|
||||
import 'package:cw_haven/api/transaction_history.dart' as transaction_history;
|
||||
|
@ -37,8 +36,8 @@ const moneroBlockSize = 1000;
|
|||
|
||||
class HavenWallet = HavenWalletBase with _$HavenWallet;
|
||||
|
||||
abstract class HavenWalletBase extends WalletBase<MoneroBalance,
|
||||
HavenTransactionHistory, HavenTransactionInfo> with Store {
|
||||
abstract class HavenWalletBase
|
||||
extends WalletBase<MoneroBalance, HavenTransactionHistory, HavenTransactionInfo> with Store {
|
||||
HavenWalletBase({required WalletInfo walletInfo})
|
||||
: balance = ObservableMap.of(getHavenBalance(accountIndex: 0)),
|
||||
_isTransactionUpdating = false,
|
||||
|
@ -47,8 +46,7 @@ abstract class HavenWalletBase extends WalletBase<MoneroBalance,
|
|||
syncStatus = NotConnectedSyncStatus(),
|
||||
super(walletInfo) {
|
||||
transactionHistory = HavenTransactionHistory();
|
||||
_onAccountChangeReaction = reaction((_) => walletAddresses.account,
|
||||
(Account? account) {
|
||||
_onAccountChangeReaction = reaction((_) => walletAddresses.account, (Account? account) {
|
||||
if (account == null) {
|
||||
return;
|
||||
}
|
||||
|
@ -96,14 +94,12 @@ abstract class HavenWalletBase extends WalletBase<MoneroBalance,
|
|||
haven_wallet.setRecoveringFromSeed(isRecovery: walletInfo.isRecovery);
|
||||
|
||||
if (haven_wallet.getCurrentHeight() <= 1) {
|
||||
haven_wallet.setRefreshFromBlockHeight(
|
||||
height: walletInfo.restoreHeight);
|
||||
haven_wallet.setRefreshFromBlockHeight(height: walletInfo.restoreHeight);
|
||||
}
|
||||
}
|
||||
|
||||
_autoSaveTimer = Timer.periodic(
|
||||
Duration(seconds: _autoSaveInterval),
|
||||
(_) async => await save());
|
||||
_autoSaveTimer =
|
||||
Timer.periodic(Duration(seconds: _autoSaveInterval), (_) async => await save());
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -115,7 +111,7 @@ abstract class HavenWalletBase extends WalletBase<MoneroBalance,
|
|||
_onAccountChangeReaction?.reaction.dispose();
|
||||
_autoSaveTimer?.cancel();
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
Future<void> connectToNode({required Node node}) async {
|
||||
try {
|
||||
|
@ -170,26 +166,25 @@ abstract class HavenWalletBase extends WalletBase<MoneroBalance,
|
|||
}
|
||||
|
||||
if (hasMultiDestination) {
|
||||
if (outputs.any((item) => item.sendAll
|
||||
|| (item.formattedCryptoAmount ?? 0) <= 0)) {
|
||||
throw HavenTransactionCreationException('You do not have enough coins to send this amount.');
|
||||
if (outputs.any((item) => item.sendAll || (item.formattedCryptoAmount ?? 0) <= 0)) {
|
||||
throw HavenTransactionCreationException(
|
||||
'You do not have enough coins to send this amount.');
|
||||
}
|
||||
|
||||
final int totalAmount = outputs.fold(0, (acc, value) =>
|
||||
acc + (value.formattedCryptoAmount ?? 0));
|
||||
final int totalAmount =
|
||||
outputs.fold(0, (acc, value) => acc + (value.formattedCryptoAmount ?? 0));
|
||||
|
||||
if (unlockedBalance < totalAmount) {
|
||||
throw HavenTransactionCreationException('You do not have enough coins to send this amount.');
|
||||
throw HavenTransactionCreationException(
|
||||
'You do not have enough coins to send this amount.');
|
||||
}
|
||||
|
||||
final moneroOutputs = outputs.map((output) =>
|
||||
MoneroOutput(
|
||||
address: output.address,
|
||||
amount: output.cryptoAmount!.replaceAll(',', '.')))
|
||||
final moneroOutputs = outputs
|
||||
.map((output) => MoneroOutput(
|
||||
address: output.address, amount: output.cryptoAmount!.replaceAll(',', '.')))
|
||||
.toList();
|
||||
|
||||
pendingTransactionDescription =
|
||||
await transaction_history.createTransactionMultDest(
|
||||
pendingTransactionDescription = await transaction_history.createTransactionMultDest(
|
||||
outputs: moneroOutputs,
|
||||
priorityRaw: _credentials.priority.serialize(),
|
||||
accountIndex: walletAddresses.account!.id);
|
||||
|
@ -198,12 +193,8 @@ abstract class HavenWalletBase extends WalletBase<MoneroBalance,
|
|||
final address = output.isParsedAddress && (output.extractedAddress?.isNotEmpty ?? false)
|
||||
? output.extractedAddress!
|
||||
: output.address;
|
||||
final amount = output.sendAll
|
||||
? null
|
||||
: output.cryptoAmount!.replaceAll(',', '.');
|
||||
final int? formattedAmount = output.sendAll
|
||||
? null
|
||||
: output.formattedCryptoAmount;
|
||||
final amount = output.sendAll ? null : output.cryptoAmount!.replaceAll(',', '.');
|
||||
final int? formattedAmount = output.sendAll ? null : output.formattedCryptoAmount;
|
||||
|
||||
if ((formattedAmount != null && unlockedBalance < formattedAmount) ||
|
||||
(formattedAmount == null && unlockedBalance <= 0)) {
|
||||
|
@ -213,8 +204,7 @@ abstract class HavenWalletBase extends WalletBase<MoneroBalance,
|
|||
'You do not have enough unlocked balance. Unlocked: $formattedBalance. Transaction amount: ${output.cryptoAmount}.');
|
||||
}
|
||||
|
||||
pendingTransactionDescription =
|
||||
await transaction_history.createTransaction(
|
||||
pendingTransactionDescription = await transaction_history.createTransaction(
|
||||
address: address,
|
||||
assetType: _credentials.assetType,
|
||||
amount: amount,
|
||||
|
@ -307,16 +297,14 @@ abstract class HavenWalletBase extends WalletBase<MoneroBalance,
|
|||
}
|
||||
|
||||
String getTransactionAddress(int accountIndex, int addressIndex) =>
|
||||
haven_wallet.getAddress(
|
||||
accountIndex: accountIndex,
|
||||
addressIndex: addressIndex);
|
||||
haven_wallet.getAddress(accountIndex: accountIndex, addressIndex: addressIndex);
|
||||
|
||||
@override
|
||||
Future<Map<String, HavenTransactionInfo>> fetchTransactions() async {
|
||||
haven_transaction_history.refreshTransactions();
|
||||
return _getAllTransactions(null).fold<Map<String, HavenTransactionInfo>>(
|
||||
<String, HavenTransactionInfo>{},
|
||||
(Map<String, HavenTransactionInfo> acc, HavenTransactionInfo tx) {
|
||||
return _getAllTransactions(null)
|
||||
.fold<Map<String, HavenTransactionInfo>>(<String, HavenTransactionInfo>{},
|
||||
(Map<String, HavenTransactionInfo> acc, HavenTransactionInfo tx) {
|
||||
acc[tx.id] = tx;
|
||||
return acc;
|
||||
});
|
||||
|
@ -340,9 +328,9 @@ abstract class HavenWalletBase extends WalletBase<MoneroBalance,
|
|||
}
|
||||
|
||||
List<HavenTransactionInfo> _getAllTransactions(dynamic _) => haven_transaction_history
|
||||
.getAllTransations()
|
||||
.map((row) => HavenTransactionInfo.fromRow(row))
|
||||
.toList();
|
||||
.getAllTransations()
|
||||
.map((row) => HavenTransactionInfo.fromRow(row))
|
||||
.toList();
|
||||
|
||||
void _setListeners() {
|
||||
_listener?.stop();
|
||||
|
@ -364,8 +352,7 @@ abstract class HavenWalletBase extends WalletBase<MoneroBalance,
|
|||
}
|
||||
|
||||
int _getHeightDistance(DateTime date) {
|
||||
final distance =
|
||||
DateTime.now().millisecondsSinceEpoch - date.millisecondsSinceEpoch;
|
||||
final distance = DateTime.now().millisecondsSinceEpoch - date.millisecondsSinceEpoch;
|
||||
final daysTmp = (distance / 86400).round();
|
||||
final days = daysTmp < 1 ? 1 : daysTmp;
|
||||
|
||||
|
@ -386,8 +373,7 @@ abstract class HavenWalletBase extends WalletBase<MoneroBalance,
|
|||
void _askForUpdateBalance() =>
|
||||
balance.addAll(getHavenBalance(accountIndex: walletAddresses.account!.id));
|
||||
|
||||
Future<void> _askForUpdateTransactionHistory() async =>
|
||||
await updateTransactions();
|
||||
Future<void> _askForUpdateTransactionHistory() async => await updateTransactions();
|
||||
|
||||
void _onNewBlock(int height, int blocksLeft, double ptc) async {
|
||||
try {
|
||||
|
@ -404,9 +390,9 @@ abstract class HavenWalletBase extends WalletBase<MoneroBalance,
|
|||
syncStatus = SyncedSyncStatus();
|
||||
|
||||
if (!_hasSyncAfterStartup) {
|
||||
_hasSyncAfterStartup = true;
|
||||
await save();
|
||||
}
|
||||
_hasSyncAfterStartup = true;
|
||||
await save();
|
||||
}
|
||||
|
||||
if (walletInfo.isRecovery) {
|
||||
await setAsRecovered();
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include <fstream>
|
||||
#include <unistd.h>
|
||||
#include <mutex>
|
||||
#include <list>
|
||||
#include "thread"
|
||||
#include "CwWalletListener.h"
|
||||
#if __APPLE__
|
||||
|
@ -138,7 +139,7 @@ extern "C"
|
|||
int8_t direction;
|
||||
int8_t isPending;
|
||||
uint32_t subaddrIndex;
|
||||
|
||||
|
||||
char *hash;
|
||||
char *paymentId;
|
||||
|
||||
|
@ -153,7 +154,7 @@ extern "C"
|
|||
std::set<uint32_t>::iterator it = transaction->subaddrIndex().begin();
|
||||
subaddrIndex = *it;
|
||||
confirmations = transaction->confirmations();
|
||||
datetime = static_cast<int64_t>(transaction->timestamp());
|
||||
datetime = static_cast<int64_t>(transaction->timestamp());
|
||||
direction = transaction->direction();
|
||||
isPending = static_cast<int8_t>(transaction->isPending());
|
||||
std::string *hash_str = new std::string(transaction->hash());
|
||||
|
@ -182,6 +183,62 @@ extern "C"
|
|||
}
|
||||
};
|
||||
|
||||
struct CoinsInfoRow
|
||||
{
|
||||
uint64_t blockHeight;
|
||||
char *hash;
|
||||
uint64_t internalOutputIndex;
|
||||
uint64_t globalOutputIndex;
|
||||
bool spent;
|
||||
bool frozen;
|
||||
uint64_t spentHeight;
|
||||
uint64_t amount;
|
||||
bool rct;
|
||||
bool keyImageKnown;
|
||||
uint64_t pkIndex;
|
||||
uint32_t subaddrIndex;
|
||||
uint32_t subaddrAccount;
|
||||
char *address;
|
||||
char *addressLabel;
|
||||
char *keyImage;
|
||||
uint64_t unlockTime;
|
||||
bool unlocked;
|
||||
char *pubKey;
|
||||
bool coinbase;
|
||||
char *description;
|
||||
|
||||
CoinsInfoRow(Monero::CoinsInfo *coinsInfo)
|
||||
{
|
||||
blockHeight = coinsInfo->blockHeight();
|
||||
std::string *hash_str = new std::string(coinsInfo->hash());
|
||||
hash = strdup(hash_str->c_str());
|
||||
internalOutputIndex = coinsInfo->internalOutputIndex();
|
||||
globalOutputIndex = coinsInfo->globalOutputIndex();
|
||||
spent = coinsInfo->spent();
|
||||
frozen = coinsInfo->frozen();
|
||||
spentHeight = coinsInfo->spentHeight();
|
||||
amount = coinsInfo->amount();
|
||||
rct = coinsInfo->rct();
|
||||
keyImageKnown = coinsInfo->keyImageKnown();
|
||||
pkIndex = coinsInfo->pkIndex();
|
||||
subaddrIndex = coinsInfo->subaddrIndex();
|
||||
subaddrAccount = coinsInfo->subaddrAccount();
|
||||
address = strdup(coinsInfo->address().c_str()) ;
|
||||
addressLabel = strdup(coinsInfo->addressLabel().c_str());
|
||||
keyImage = strdup(coinsInfo->keyImage().c_str());
|
||||
unlockTime = coinsInfo->unlockTime();
|
||||
unlocked = coinsInfo->unlocked();
|
||||
pubKey = strdup(coinsInfo->pubKey().c_str());
|
||||
coinbase = coinsInfo->coinbase();
|
||||
description = strdup(coinsInfo->description().c_str());
|
||||
}
|
||||
|
||||
void setUnlocked(bool unlocked);
|
||||
|
||||
};
|
||||
|
||||
Monero::Coins *m_coins;
|
||||
|
||||
Monero::Wallet *m_wallet;
|
||||
Monero::TransactionHistory *m_transaction_history;
|
||||
MoneroWalletListener *m_listener;
|
||||
|
@ -189,6 +246,7 @@ extern "C"
|
|||
Monero::SubaddressAccount *m_account;
|
||||
uint64_t m_last_known_wallet_height;
|
||||
uint64_t m_cached_syncing_blockchain_height = 0;
|
||||
std::list<Monero::CoinsInfo*> m_coins_info;
|
||||
std::mutex store_lock;
|
||||
bool is_storing = false;
|
||||
|
||||
|
@ -196,7 +254,7 @@ extern "C"
|
|||
{
|
||||
m_wallet = wallet;
|
||||
m_listener = nullptr;
|
||||
|
||||
|
||||
|
||||
if (wallet != nullptr)
|
||||
{
|
||||
|
@ -224,6 +282,17 @@ extern "C"
|
|||
{
|
||||
m_subaddress = nullptr;
|
||||
}
|
||||
|
||||
m_coins_info = std::list<Monero::CoinsInfo*>();
|
||||
|
||||
if (wallet != nullptr)
|
||||
{
|
||||
m_coins = wallet->coins();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_coins = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
Monero::Wallet *get_current_wallet()
|
||||
|
@ -410,7 +479,7 @@ extern "C"
|
|||
{
|
||||
nice(19);
|
||||
Monero::Wallet *wallet = get_current_wallet();
|
||||
|
||||
|
||||
std::string _login = "";
|
||||
std::string _password = "";
|
||||
std::string _socksProxyAddress = "";
|
||||
|
@ -487,10 +556,19 @@ extern "C"
|
|||
}
|
||||
|
||||
bool transaction_create(char *address, char *payment_id, char *amount,
|
||||
uint8_t priority_raw, uint32_t subaddr_account, Utf8Box &error, PendingTransactionRaw &pendingTransaction)
|
||||
uint8_t priority_raw, uint32_t subaddr_account,
|
||||
char **preferred_inputs, uint32_t preferred_inputs_size,
|
||||
Utf8Box &error, PendingTransactionRaw &pendingTransaction)
|
||||
{
|
||||
nice(19);
|
||||
|
||||
|
||||
std::set<std::string> _preferred_inputs;
|
||||
|
||||
for (int i = 0; i < preferred_inputs_size; i++) {
|
||||
_preferred_inputs.insert(std::string(*preferred_inputs));
|
||||
preferred_inputs++;
|
||||
}
|
||||
|
||||
auto priority = static_cast<Monero::PendingTransaction::Priority>(priority_raw);
|
||||
std::string _payment_id;
|
||||
Monero::PendingTransaction *transaction;
|
||||
|
@ -503,13 +581,13 @@ extern "C"
|
|||
if (amount != nullptr)
|
||||
{
|
||||
uint64_t _amount = Monero::Wallet::amountFromString(std::string(amount));
|
||||
transaction = m_wallet->createTransaction(std::string(address), _payment_id, _amount, m_wallet->defaultMixin(), priority, subaddr_account);
|
||||
transaction = m_wallet->createTransaction(std::string(address), _payment_id, _amount, m_wallet->defaultMixin(), priority, subaddr_account, {}, _preferred_inputs);
|
||||
}
|
||||
else
|
||||
{
|
||||
transaction = m_wallet->createTransaction(std::string(address), _payment_id, Monero::optional<uint64_t>(), m_wallet->defaultMixin(), priority, subaddr_account);
|
||||
transaction = m_wallet->createTransaction(std::string(address), _payment_id, Monero::optional<uint64_t>(), m_wallet->defaultMixin(), priority, subaddr_account, {}, _preferred_inputs);
|
||||
}
|
||||
|
||||
|
||||
int status = transaction->status();
|
||||
|
||||
if (status == Monero::PendingTransaction::Status::Status_Error || status == Monero::PendingTransaction::Status::Status_Critical)
|
||||
|
@ -527,7 +605,9 @@ extern "C"
|
|||
}
|
||||
|
||||
bool transaction_create_mult_dest(char **addresses, char *payment_id, char **amounts, uint32_t size,
|
||||
uint8_t priority_raw, uint32_t subaddr_account, Utf8Box &error, PendingTransactionRaw &pendingTransaction)
|
||||
uint8_t priority_raw, uint32_t subaddr_account,
|
||||
char **preferred_inputs, uint32_t preferred_inputs_size,
|
||||
Utf8Box &error, PendingTransactionRaw &pendingTransaction)
|
||||
{
|
||||
nice(19);
|
||||
|
||||
|
@ -541,6 +621,13 @@ extern "C"
|
|||
amounts++;
|
||||
}
|
||||
|
||||
std::set<std::string> _preferred_inputs;
|
||||
|
||||
for (int i = 0; i < preferred_inputs_size; i++) {
|
||||
_preferred_inputs.insert(std::string(*preferred_inputs));
|
||||
preferred_inputs++;
|
||||
}
|
||||
|
||||
auto priority = static_cast<Monero::PendingTransaction::Priority>(priority_raw);
|
||||
std::string _payment_id;
|
||||
Monero::PendingTransaction *transaction;
|
||||
|
@ -800,6 +887,91 @@ extern "C"
|
|||
return m_wallet->trustedDaemon();
|
||||
}
|
||||
|
||||
CoinsInfoRow* coin(int index)
|
||||
{
|
||||
if (index >= 0 && index < m_coins_info.size()) {
|
||||
std::list<Monero::CoinsInfo*>::iterator it = m_coins_info.begin();
|
||||
std::advance(it, index);
|
||||
Monero::CoinsInfo* element = *it;
|
||||
std::cout << "Element at index " << index << ": " << element << std::endl;
|
||||
return new CoinsInfoRow(element);
|
||||
} else {
|
||||
std::cout << "Invalid index." << std::endl;
|
||||
return nullptr; // Return a default value (nullptr) for invalid index
|
||||
}
|
||||
}
|
||||
|
||||
void refresh_coins(uint32_t accountIndex)
|
||||
{
|
||||
m_coins_info.clear();
|
||||
|
||||
m_coins->refresh();
|
||||
for (const auto i : m_coins->getAll()) {
|
||||
if (i->subaddrAccount() == accountIndex && !(i->spent())) {
|
||||
m_coins_info.push_back(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t coins_count()
|
||||
{
|
||||
return m_coins_info.size();
|
||||
}
|
||||
|
||||
CoinsInfoRow** coins_from_account(uint32_t accountIndex)
|
||||
{
|
||||
std::vector<CoinsInfoRow*> matchingCoins;
|
||||
|
||||
for (int i = 0; i < coins_count(); i++) {
|
||||
CoinsInfoRow* coinInfo = coin(i);
|
||||
if (coinInfo->subaddrAccount == accountIndex) {
|
||||
matchingCoins.push_back(coinInfo);
|
||||
}
|
||||
}
|
||||
|
||||
CoinsInfoRow** result = new CoinsInfoRow*[matchingCoins.size()];
|
||||
std::copy(matchingCoins.begin(), matchingCoins.end(), result);
|
||||
return result;
|
||||
}
|
||||
|
||||
CoinsInfoRow** coins_from_txid(const char* txid, size_t* count)
|
||||
{
|
||||
std::vector<CoinsInfoRow*> matchingCoins;
|
||||
|
||||
for (int i = 0; i < coins_count(); i++) {
|
||||
CoinsInfoRow* coinInfo = coin(i);
|
||||
if (std::string(coinInfo->hash) == txid) {
|
||||
matchingCoins.push_back(coinInfo);
|
||||
}
|
||||
}
|
||||
|
||||
*count = matchingCoins.size();
|
||||
CoinsInfoRow** result = new CoinsInfoRow*[*count];
|
||||
std::copy(matchingCoins.begin(), matchingCoins.end(), result);
|
||||
return result;
|
||||
}
|
||||
|
||||
CoinsInfoRow** coins_from_key_image(const char** keyimages, size_t keyimageCount, size_t* count)
|
||||
{
|
||||
std::vector<CoinsInfoRow*> matchingCoins;
|
||||
|
||||
for (int i = 0; i < coins_count(); i++) {
|
||||
CoinsInfoRow* coinsInfoRow = coin(i);
|
||||
for (size_t j = 0; j < keyimageCount; j++) {
|
||||
if (coinsInfoRow->keyImageKnown && std::string(coinsInfoRow->keyImage) == keyimages[j]) {
|
||||
matchingCoins.push_back(coinsInfoRow);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*count = matchingCoins.size();
|
||||
CoinsInfoRow** result = new CoinsInfoRow*[*count];
|
||||
std::copy(matchingCoins.begin(), matchingCoins.end(), result);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
23
cw_monero/lib/api/coins_info.dart
Normal file
23
cw_monero/lib/api/coins_info.dart
Normal file
|
@ -0,0 +1,23 @@
|
|||
import 'dart:ffi';
|
||||
import 'package:cw_monero/api/signatures.dart';
|
||||
import 'package:cw_monero/api/structs/coins_info_row.dart';
|
||||
import 'package:cw_monero/api/types.dart';
|
||||
import 'package:cw_monero/api/monero_api.dart';
|
||||
|
||||
final refreshCoinsNative = moneroApi
|
||||
.lookup<NativeFunction<refresh_coins>>('refresh_coins')
|
||||
.asFunction<RefreshCoins>();
|
||||
|
||||
final coinsCountNative = moneroApi
|
||||
.lookup<NativeFunction<coins_count>>('coins_count')
|
||||
.asFunction<CoinsCount>();
|
||||
|
||||
final coinNative = moneroApi
|
||||
.lookup<NativeFunction<coin>>('coin')
|
||||
.asFunction<GetCoin>();
|
||||
|
||||
void refreshCoins(int accountIndex) => refreshCoinsNative(accountIndex);
|
||||
|
||||
int countOfCoins() => coinsCountNative();
|
||||
|
||||
CoinsInfoRow getCoin(int index) => coinNative(index).ref;
|
|
@ -1,4 +1,5 @@
|
|||
import 'dart:ffi';
|
||||
import 'package:cw_monero/api/structs/coins_info_row.dart';
|
||||
import 'package:cw_monero/api/structs/pending_transaction.dart';
|
||||
import 'package:cw_monero/api/structs/ut8_box.dart';
|
||||
import 'package:ffi/ffi.dart';
|
||||
|
@ -9,8 +10,8 @@ typedef create_wallet = Int8 Function(
|
|||
typedef restore_wallet_from_seed = Int8 Function(
|
||||
Pointer<Utf8>, Pointer<Utf8>, Pointer<Utf8>, Int32, Int64, Pointer<Utf8>);
|
||||
|
||||
typedef restore_wallet_from_keys = Int8 Function(Pointer<Utf8>, Pointer<Utf8>,
|
||||
Pointer<Utf8>, Pointer<Utf8>, Pointer<Utf8>, Pointer<Utf8>, Int32, Int64, Pointer<Utf8>);
|
||||
typedef restore_wallet_from_keys = Int8 Function(Pointer<Utf8>, Pointer<Utf8>, Pointer<Utf8>,
|
||||
Pointer<Utf8>, Pointer<Utf8>, Pointer<Utf8>, Int32, Int64, Pointer<Utf8>);
|
||||
|
||||
typedef is_wallet_exist = Int8 Function(Pointer<Utf8>);
|
||||
|
||||
|
@ -63,8 +64,7 @@ typedef subaddrress_refresh = Void Function(Int32);
|
|||
|
||||
typedef subaddress_get_all = Pointer<Int64> Function();
|
||||
|
||||
typedef subaddress_add_new = Void Function(
|
||||
Int32 accountIndex, Pointer<Utf8> label);
|
||||
typedef subaddress_add_new = Void Function(Int32 accountIndex, Pointer<Utf8> label);
|
||||
|
||||
typedef subaddress_set_label = Void Function(
|
||||
Int32 accountIndex, Int32 addressIndex, Pointer<Utf8> label);
|
||||
|
@ -77,8 +77,7 @@ typedef account_get_all = Pointer<Int64> Function();
|
|||
|
||||
typedef account_add_new = Void Function(Pointer<Utf8> label);
|
||||
|
||||
typedef account_set_label = Void Function(
|
||||
Int32 accountIndex, Pointer<Utf8> label);
|
||||
typedef account_set_label = Void Function(Int32 accountIndex, Pointer<Utf8> label);
|
||||
|
||||
typedef transactions_refresh = Void Function();
|
||||
|
||||
|
@ -94,6 +93,8 @@ typedef transaction_create = Int8 Function(
|
|||
Pointer<Utf8> amount,
|
||||
Int8 priorityRaw,
|
||||
Int32 subaddrAccount,
|
||||
Pointer<Pointer<Utf8>> preferredInputs,
|
||||
Int32 preferredInputsSize,
|
||||
Pointer<Utf8Box> error,
|
||||
Pointer<PendingTransactionRaw> pendingTransaction);
|
||||
|
||||
|
@ -104,6 +105,8 @@ typedef transaction_create_mult_dest = Int8 Function(
|
|||
Int32 size,
|
||||
Int8 priorityRaw,
|
||||
Int32 subaddrAccount,
|
||||
Pointer<Pointer<Utf8>> preferredInputs,
|
||||
Int32 preferredInputsSize,
|
||||
Pointer<Utf8Box> error,
|
||||
Pointer<PendingTransactionRaw> pendingTransaction);
|
||||
|
||||
|
@ -123,10 +126,16 @@ typedef on_startup = Void Function();
|
|||
|
||||
typedef rescan_blockchain = Void Function();
|
||||
|
||||
typedef get_subaddress_label = Pointer<Utf8> Function(
|
||||
Int32 accountIndex,
|
||||
Int32 addressIndex);
|
||||
typedef get_subaddress_label = Pointer<Utf8> Function(Int32 accountIndex, Int32 addressIndex);
|
||||
|
||||
typedef set_trusted_daemon = Void Function(Int8 trusted);
|
||||
|
||||
typedef trusted_daemon = Int8 Function();
|
||||
typedef trusted_daemon = Int8 Function();
|
||||
|
||||
typedef refresh_coins = Void Function(Int32 accountIndex);
|
||||
|
||||
typedef coins_count = Int64 Function();
|
||||
|
||||
// typedef coins_from_txid = Pointer<CoinsInfoRow> Function(Pointer<Utf8> txid);
|
||||
|
||||
typedef coin = Pointer<CoinsInfoRow> Function(Int32 index);
|
||||
|
|
73
cw_monero/lib/api/structs/coins_info_row.dart
Normal file
73
cw_monero/lib/api/structs/coins_info_row.dart
Normal file
|
@ -0,0 +1,73 @@
|
|||
import 'dart:ffi';
|
||||
import 'package:ffi/ffi.dart';
|
||||
|
||||
class CoinsInfoRow extends Struct {
|
||||
@Int64()
|
||||
external int blockHeight;
|
||||
|
||||
external Pointer<Utf8> hash;
|
||||
|
||||
@Uint64()
|
||||
external int internalOutputIndex;
|
||||
|
||||
@Uint64()
|
||||
external int globalOutputIndex;
|
||||
|
||||
@Int8()
|
||||
external int spent;
|
||||
|
||||
@Int8()
|
||||
external int frozen;
|
||||
|
||||
@Uint64()
|
||||
external int spentHeight;
|
||||
|
||||
@Uint64()
|
||||
external int amount;
|
||||
|
||||
@Int8()
|
||||
external int rct;
|
||||
|
||||
@Int8()
|
||||
external int keyImageKnown;
|
||||
|
||||
@Uint64()
|
||||
external int pkIndex;
|
||||
|
||||
@Uint32()
|
||||
external int subaddrIndex;
|
||||
|
||||
@Uint32()
|
||||
external int subaddrAccount;
|
||||
|
||||
external Pointer<Utf8> address;
|
||||
|
||||
external Pointer<Utf8> addressLabel;
|
||||
|
||||
external Pointer<Utf8> keyImage;
|
||||
|
||||
@Uint64()
|
||||
external int unlockTime;
|
||||
|
||||
@Int8()
|
||||
external int unlocked;
|
||||
|
||||
external Pointer<Utf8> pubKey;
|
||||
|
||||
@Int8()
|
||||
external int coinbase;
|
||||
|
||||
external Pointer<Utf8> description;
|
||||
|
||||
String getHash() => hash.toDartString();
|
||||
|
||||
String getAddress() => address.toDartString();
|
||||
|
||||
String getAddressLabel() => addressLabel.toDartString();
|
||||
|
||||
String getKeyImage() => keyImage.toDartString();
|
||||
|
||||
String getPubKey() => pubKey.toDartString();
|
||||
|
||||
String getDescription() => description.toDartString();
|
||||
}
|
|
@ -35,9 +35,8 @@ final transactionCommitNative = moneroApi
|
|||
.lookup<NativeFunction<transaction_commit>>('transaction_commit')
|
||||
.asFunction<TransactionCommit>();
|
||||
|
||||
final getTxKeyNative = moneroApi
|
||||
.lookup<NativeFunction<get_tx_key>>('get_tx_key')
|
||||
.asFunction<GetTxKey>();
|
||||
final getTxKeyNative =
|
||||
moneroApi.lookup<NativeFunction<get_tx_key>>('get_tx_key').asFunction<GetTxKey>();
|
||||
|
||||
String getTxKey(String txId) {
|
||||
final txIdPointer = txId.toNativeUtf8();
|
||||
|
@ -71,10 +70,21 @@ PendingTransactionDescription createTransactionSync(
|
|||
required String paymentId,
|
||||
required int priorityRaw,
|
||||
String? amount,
|
||||
int accountIndex = 0}) {
|
||||
int accountIndex = 0,
|
||||
List<String> preferredInputs = const []}) {
|
||||
final addressPointer = address.toNativeUtf8();
|
||||
final paymentIdPointer = paymentId.toNativeUtf8();
|
||||
final amountPointer = amount != null ? amount.toNativeUtf8() : nullptr;
|
||||
|
||||
final int preferredInputsSize = preferredInputs.length;
|
||||
final List<Pointer<Utf8>> preferredInputsPointers =
|
||||
preferredInputs.map((output) => output.toNativeUtf8()).toList();
|
||||
final Pointer<Pointer<Utf8>> preferredInputsPointerPointer = calloc(preferredInputsSize);
|
||||
|
||||
for (int i = 0; i < preferredInputsSize; i++) {
|
||||
preferredInputsPointerPointer[i] = preferredInputsPointers[i];
|
||||
}
|
||||
|
||||
final errorMessagePointer = calloc<Utf8Box>();
|
||||
final pendingTransactionRawPointer = calloc<PendingTransactionRaw>();
|
||||
final created = transactionCreateNative(
|
||||
|
@ -83,10 +93,16 @@ PendingTransactionDescription createTransactionSync(
|
|||
amountPointer,
|
||||
priorityRaw,
|
||||
accountIndex,
|
||||
preferredInputsPointerPointer,
|
||||
preferredInputsSize,
|
||||
errorMessagePointer,
|
||||
pendingTransactionRawPointer) !=
|
||||
0;
|
||||
|
||||
calloc.free(preferredInputsPointerPointer);
|
||||
|
||||
preferredInputsPointers.forEach((element) => calloc.free(element));
|
||||
|
||||
calloc.free(addressPointer);
|
||||
calloc.free(paymentIdPointer);
|
||||
|
||||
|
@ -111,15 +127,16 @@ PendingTransactionDescription createTransactionSync(
|
|||
|
||||
PendingTransactionDescription createTransactionMultDestSync(
|
||||
{required List<MoneroOutput> outputs,
|
||||
required String paymentId,
|
||||
required int priorityRaw,
|
||||
int accountIndex = 0}) {
|
||||
required String paymentId,
|
||||
required int priorityRaw,
|
||||
int accountIndex = 0,
|
||||
List<String> preferredInputs = const []}) {
|
||||
final int size = outputs.length;
|
||||
final List<Pointer<Utf8>> addressesPointers = outputs.map((output) =>
|
||||
output.address.toNativeUtf8()).toList();
|
||||
final List<Pointer<Utf8>> addressesPointers =
|
||||
outputs.map((output) => output.address.toNativeUtf8()).toList();
|
||||
final Pointer<Pointer<Utf8>> addressesPointerPointer = calloc(size);
|
||||
final List<Pointer<Utf8>> amountsPointers = outputs.map((output) =>
|
||||
output.amount.toNativeUtf8()).toList();
|
||||
final List<Pointer<Utf8>> amountsPointers =
|
||||
outputs.map((output) => output.amount.toNativeUtf8()).toList();
|
||||
final Pointer<Pointer<Utf8>> amountsPointerPointer = calloc(size);
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
|
@ -127,25 +144,38 @@ PendingTransactionDescription createTransactionMultDestSync(
|
|||
amountsPointerPointer[i] = amountsPointers[i];
|
||||
}
|
||||
|
||||
final int preferredInputsSize = preferredInputs.length;
|
||||
final List<Pointer<Utf8>> preferredInputsPointers =
|
||||
preferredInputs.map((output) => output.toNativeUtf8()).toList();
|
||||
final Pointer<Pointer<Utf8>> preferredInputsPointerPointer = calloc(preferredInputsSize);
|
||||
|
||||
for (int i = 0; i < preferredInputsSize; i++) {
|
||||
preferredInputsPointerPointer[i] = preferredInputsPointers[i];
|
||||
}
|
||||
|
||||
final paymentIdPointer = paymentId.toNativeUtf8();
|
||||
final errorMessagePointer = calloc<Utf8Box>();
|
||||
final pendingTransactionRawPointer = calloc<PendingTransactionRaw>();
|
||||
final created = transactionCreateMultDestNative(
|
||||
addressesPointerPointer,
|
||||
paymentIdPointer,
|
||||
amountsPointerPointer,
|
||||
size,
|
||||
priorityRaw,
|
||||
accountIndex,
|
||||
errorMessagePointer,
|
||||
pendingTransactionRawPointer) !=
|
||||
addressesPointerPointer,
|
||||
paymentIdPointer,
|
||||
amountsPointerPointer,
|
||||
size,
|
||||
priorityRaw,
|
||||
accountIndex,
|
||||
preferredInputsPointerPointer,
|
||||
preferredInputsSize,
|
||||
errorMessagePointer,
|
||||
pendingTransactionRawPointer) !=
|
||||
0;
|
||||
|
||||
calloc.free(addressesPointerPointer);
|
||||
calloc.free(amountsPointerPointer);
|
||||
calloc.free(preferredInputsPointerPointer);
|
||||
|
||||
addressesPointers.forEach((element) => calloc.free(element));
|
||||
amountsPointers.forEach((element) => calloc.free(element));
|
||||
preferredInputsPointers.forEach((element) => calloc.free(element));
|
||||
|
||||
calloc.free(paymentIdPointer);
|
||||
|
||||
|
@ -164,13 +194,12 @@ PendingTransactionDescription createTransactionMultDestSync(
|
|||
pointerAddress: pendingTransactionRawPointer.address);
|
||||
}
|
||||
|
||||
void commitTransactionFromPointerAddress({required int address}) => commitTransaction(
|
||||
transactionPointer: Pointer<PendingTransactionRaw>.fromAddress(address));
|
||||
void commitTransactionFromPointerAddress({required int address}) =>
|
||||
commitTransaction(transactionPointer: Pointer<PendingTransactionRaw>.fromAddress(address));
|
||||
|
||||
void commitTransaction({required Pointer<PendingTransactionRaw> transactionPointer}) {
|
||||
final errorMessagePointer = calloc<Utf8Box>();
|
||||
final isCommited =
|
||||
transactionCommitNative(transactionPointer, errorMessagePointer) != 0;
|
||||
final isCommited = transactionCommitNative(transactionPointer, errorMessagePointer) != 0;
|
||||
|
||||
if (!isCommited) {
|
||||
final message = errorMessagePointer.ref.getValue();
|
||||
|
@ -185,13 +214,15 @@ PendingTransactionDescription _createTransactionSync(Map args) {
|
|||
final amount = args['amount'] as String?;
|
||||
final priorityRaw = args['priorityRaw'] as int;
|
||||
final accountIndex = args['accountIndex'] as int;
|
||||
final preferredInputs = args['preferredInputs'] as List<String>;
|
||||
|
||||
return createTransactionSync(
|
||||
address: address,
|
||||
paymentId: paymentId,
|
||||
amount: amount,
|
||||
priorityRaw: priorityRaw,
|
||||
accountIndex: accountIndex);
|
||||
accountIndex: accountIndex,
|
||||
preferredInputs: preferredInputs);
|
||||
}
|
||||
|
||||
PendingTransactionDescription _createTransactionMultDestSync(Map args) {
|
||||
|
@ -199,12 +230,14 @@ PendingTransactionDescription _createTransactionMultDestSync(Map args) {
|
|||
final paymentId = args['paymentId'] as String;
|
||||
final priorityRaw = args['priorityRaw'] as int;
|
||||
final accountIndex = args['accountIndex'] as int;
|
||||
final preferredInputs = args['preferredInputs'] as List<String>;
|
||||
|
||||
return createTransactionMultDestSync(
|
||||
outputs: outputs,
|
||||
paymentId: paymentId,
|
||||
priorityRaw: priorityRaw,
|
||||
accountIndex: accountIndex);
|
||||
accountIndex: accountIndex,
|
||||
preferredInputs: preferredInputs);
|
||||
}
|
||||
|
||||
Future<PendingTransactionDescription> createTransaction(
|
||||
|
@ -212,23 +245,27 @@ Future<PendingTransactionDescription> createTransaction(
|
|||
required int priorityRaw,
|
||||
String? amount,
|
||||
String paymentId = '',
|
||||
int accountIndex = 0}) =>
|
||||
int accountIndex = 0,
|
||||
List<String> preferredInputs = const []}) =>
|
||||
compute(_createTransactionSync, {
|
||||
'address': address,
|
||||
'paymentId': paymentId,
|
||||
'amount': amount,
|
||||
'priorityRaw': priorityRaw,
|
||||
'accountIndex': accountIndex
|
||||
'accountIndex': accountIndex,
|
||||
'preferredInputs': preferredInputs
|
||||
});
|
||||
|
||||
Future<PendingTransactionDescription> createTransactionMultDest(
|
||||
{required List<MoneroOutput> outputs,
|
||||
required int priorityRaw,
|
||||
String paymentId = '',
|
||||
int accountIndex = 0}) =>
|
||||
{required List<MoneroOutput> outputs,
|
||||
required int priorityRaw,
|
||||
String paymentId = '',
|
||||
int accountIndex = 0,
|
||||
List<String> preferredInputs = const []}) =>
|
||||
compute(_createTransactionMultDestSync, {
|
||||
'outputs': outputs,
|
||||
'paymentId': paymentId,
|
||||
'priorityRaw': priorityRaw,
|
||||
'accountIndex': accountIndex
|
||||
'accountIndex': accountIndex,
|
||||
'preferredInputs': preferredInputs
|
||||
});
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import 'dart:ffi';
|
||||
import 'package:cw_monero/api/structs/coins_info_row.dart';
|
||||
import 'package:cw_monero/api/structs/pending_transaction.dart';
|
||||
import 'package:cw_monero/api/structs/ut8_box.dart';
|
||||
import 'package:ffi/ffi.dart';
|
||||
|
@ -92,6 +93,8 @@ typedef TransactionCreate = int Function(
|
|||
Pointer<Utf8> amount,
|
||||
int priorityRaw,
|
||||
int subaddrAccount,
|
||||
Pointer<Pointer<Utf8>> preferredInputs,
|
||||
int preferredInputsSize,
|
||||
Pointer<Utf8Box> error,
|
||||
Pointer<PendingTransactionRaw> pendingTransaction);
|
||||
|
||||
|
@ -102,6 +105,8 @@ typedef TransactionCreateMultDest = int Function(
|
|||
int size,
|
||||
int priorityRaw,
|
||||
int subaddrAccount,
|
||||
Pointer<Pointer<Utf8>> preferredInputs,
|
||||
int preferredInputsSize,
|
||||
Pointer<Utf8Box> error,
|
||||
Pointer<PendingTransactionRaw> pendingTransaction);
|
||||
|
||||
|
@ -127,4 +132,10 @@ typedef GetSubaddressLabel = Pointer<Utf8> Function(
|
|||
|
||||
typedef SetTrustedDaemon = void Function(int);
|
||||
|
||||
typedef TrustedDaemon = int Function();
|
||||
typedef TrustedDaemon = int Function();
|
||||
|
||||
typedef RefreshCoins = void Function(int);
|
||||
|
||||
typedef CoinsCount = int Function();
|
||||
|
||||
typedef GetCoin = Pointer<CoinsInfoRow> Function(int);
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
class MoneroTransactionNoInputsException implements Exception {
|
||||
@override
|
||||
String toString() => 'Not enough inputs available. Please select more under Coin Control';
|
||||
}
|
|
@ -1,19 +1,20 @@
|
|||
import 'package:cw_monero/api/structs/subaddress_row.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:cw_monero/api/coins_info.dart';
|
||||
import 'package:cw_monero/api/subaddress_list.dart' as subaddress_list;
|
||||
import 'package:cw_core/subaddress.dart';
|
||||
|
||||
part 'monero_subaddress_list.g.dart';
|
||||
|
||||
class MoneroSubaddressList = MoneroSubaddressListBase
|
||||
with _$MoneroSubaddressList;
|
||||
class MoneroSubaddressList = MoneroSubaddressListBase with _$MoneroSubaddressList;
|
||||
|
||||
abstract class MoneroSubaddressListBase with Store {
|
||||
MoneroSubaddressListBase()
|
||||
: _isRefreshing = false,
|
||||
_isUpdating = false,
|
||||
subaddresses = ObservableList<Subaddress>();
|
||||
: _isRefreshing = false,
|
||||
_isUpdating = false,
|
||||
subaddresses = ObservableList<Subaddress>();
|
||||
|
||||
final List<String> _usedAddresses = [];
|
||||
|
||||
@observable
|
||||
ObservableList<Subaddress> subaddresses;
|
||||
|
@ -22,6 +23,8 @@ abstract class MoneroSubaddressListBase with Store {
|
|||
bool _isUpdating;
|
||||
|
||||
void update({required int accountIndex}) {
|
||||
refreshCoins(accountIndex);
|
||||
|
||||
if (_isUpdating) {
|
||||
return;
|
||||
}
|
||||
|
@ -47,20 +50,24 @@ abstract class MoneroSubaddressListBase with Store {
|
|||
subaddresses = [primary] + rest.toList();
|
||||
}
|
||||
|
||||
return subaddresses
|
||||
.map((subaddressRow) => Subaddress(
|
||||
return subaddresses.map((subaddressRow) {
|
||||
final hasDefaultAddressName =
|
||||
subaddressRow.getLabel().toLowerCase() == 'Primary account'.toLowerCase() ||
|
||||
subaddressRow.getLabel().toLowerCase() == 'Untitled account'.toLowerCase();
|
||||
final isPrimaryAddress = subaddressRow.getId() == 0 && hasDefaultAddressName;
|
||||
return Subaddress(
|
||||
id: subaddressRow.getId(),
|
||||
address: subaddressRow.getAddress(),
|
||||
label: subaddressRow.getId() == 0 &&
|
||||
subaddressRow.getLabel().toLowerCase() == 'Primary account'.toLowerCase()
|
||||
? 'Primary address'
|
||||
: subaddressRow.getLabel()))
|
||||
.toList();
|
||||
label: isPrimaryAddress
|
||||
? 'Primary address'
|
||||
: hasDefaultAddressName
|
||||
? ''
|
||||
: subaddressRow.getLabel());
|
||||
}).toList();
|
||||
}
|
||||
|
||||
Future<void> addSubaddress({required int accountIndex, required String label}) async {
|
||||
await subaddress_list.addSubaddress(
|
||||
accountIndex: accountIndex, label: label);
|
||||
await subaddress_list.addSubaddress(accountIndex: accountIndex, label: label);
|
||||
update(accountIndex: accountIndex);
|
||||
}
|
||||
|
||||
|
@ -86,4 +93,59 @@ abstract class MoneroSubaddressListBase with Store {
|
|||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> updateWithAutoGenerate({
|
||||
required int accountIndex,
|
||||
required String defaultLabel,
|
||||
required List<String> usedAddresses,
|
||||
}) async {
|
||||
_usedAddresses.addAll(usedAddresses);
|
||||
if (_isUpdating) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
_isUpdating = true;
|
||||
refresh(accountIndex: accountIndex);
|
||||
subaddresses.clear();
|
||||
final newSubAddresses =
|
||||
await _getAllUnusedAddresses(accountIndex: accountIndex, label: defaultLabel);
|
||||
subaddresses.addAll(newSubAddresses);
|
||||
} catch (e) {
|
||||
rethrow;
|
||||
} finally {
|
||||
_isUpdating = false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<Subaddress>> _getAllUnusedAddresses(
|
||||
{required int accountIndex, required String label}) async {
|
||||
final allAddresses = subaddress_list.getAllSubaddresses();
|
||||
|
||||
if (allAddresses.isEmpty || _usedAddresses.contains(allAddresses.last.getAddress())) {
|
||||
final isAddressUnused = await _newSubaddress(accountIndex: accountIndex, label: label);
|
||||
if (!isAddressUnused) {
|
||||
return await _getAllUnusedAddresses(accountIndex: accountIndex, label: label);
|
||||
}
|
||||
}
|
||||
|
||||
return allAddresses
|
||||
.map((subaddressRow) => Subaddress(
|
||||
id: subaddressRow.getId(),
|
||||
address: subaddressRow.getAddress(),
|
||||
label: subaddressRow.getId() == 0 &&
|
||||
subaddressRow.getLabel().toLowerCase() == 'Primary account'.toLowerCase()
|
||||
? 'Primary address'
|
||||
: subaddressRow.getLabel()))
|
||||
.toList();
|
||||
}
|
||||
|
||||
Future<bool> _newSubaddress({required int accountIndex, required String label}) async {
|
||||
await subaddress_list.addSubaddress(accountIndex: accountIndex, label: label);
|
||||
|
||||
return subaddress_list
|
||||
.getAllSubaddresses()
|
||||
.where((subaddressRow) => !_usedAddresses.contains(subaddressRow.getAddress()))
|
||||
.isNotEmpty;
|
||||
}
|
||||
}
|
||||
|
|
28
cw_monero/lib/monero_unspent.dart
Normal file
28
cw_monero/lib/monero_unspent.dart
Normal file
|
@ -0,0 +1,28 @@
|
|||
import 'package:cw_monero/api/structs/coins_info_row.dart';
|
||||
|
||||
class MoneroUnspent {
|
||||
MoneroUnspent(this.address, this.hash, this.keyImage, this.value, this.isFrozen, this.isUnlocked)
|
||||
: isSending = true,
|
||||
note = '';
|
||||
|
||||
MoneroUnspent.fromCoinsInfoRow(CoinsInfoRow coinsInfoRow)
|
||||
: address = coinsInfoRow.getAddress(),
|
||||
hash = coinsInfoRow.getHash(),
|
||||
keyImage = coinsInfoRow.getKeyImage(),
|
||||
value = coinsInfoRow.amount,
|
||||
isFrozen = coinsInfoRow.frozen == 1,
|
||||
isUnlocked = coinsInfoRow.unlocked == 1,
|
||||
isSending = true,
|
||||
note = '';
|
||||
|
||||
final String address;
|
||||
final String hash;
|
||||
final String keyImage;
|
||||
final int value;
|
||||
|
||||
final bool isUnlocked;
|
||||
|
||||
bool isFrozen;
|
||||
bool isSending;
|
||||
String note;
|
||||
}
|
|
@ -1,33 +1,35 @@
|
|||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
import 'package:cw_core/pathForWallet.dart';
|
||||
import 'package:cw_core/transaction_priority.dart';
|
||||
import 'package:cw_core/monero_amount_format.dart';
|
||||
import 'package:cw_monero/monero_transaction_creation_exception.dart';
|
||||
import 'package:cw_monero/monero_transaction_info.dart';
|
||||
import 'package:cw_monero/monero_wallet_addresses.dart';
|
||||
import 'package:cw_core/monero_wallet_utils.dart';
|
||||
import 'package:cw_monero/api/structs/pending_transaction.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:cw_monero/api/transaction_history.dart'
|
||||
as monero_transaction_history;
|
||||
import 'package:cw_monero/api/wallet.dart';
|
||||
import 'package:cw_monero/api/wallet.dart' as monero_wallet;
|
||||
import 'package:cw_monero/api/transaction_history.dart' as transaction_history;
|
||||
import 'package:cw_monero/api/monero_output.dart';
|
||||
import 'package:cw_monero/monero_transaction_creation_credentials.dart';
|
||||
import 'package:cw_monero/pending_monero_transaction.dart';
|
||||
import 'package:cw_core/monero_wallet_keys.dart';
|
||||
import 'package:cw_core/monero_balance.dart';
|
||||
import 'package:cw_monero/monero_transaction_history.dart';
|
||||
import 'package:cw_core/account.dart';
|
||||
import 'package:cw_core/pending_transaction.dart';
|
||||
import 'package:cw_core/wallet_base.dart';
|
||||
import 'package:cw_core/sync_status.dart';
|
||||
import 'package:cw_core/wallet_info.dart';
|
||||
import 'package:cw_core/node.dart';
|
||||
import 'package:cw_core/monero_transaction_priority.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/monero_amount_format.dart';
|
||||
import 'package:cw_core/monero_balance.dart';
|
||||
import 'package:cw_core/monero_transaction_priority.dart';
|
||||
import 'package:cw_core/monero_wallet_keys.dart';
|
||||
import 'package:cw_core/monero_wallet_utils.dart';
|
||||
import 'package:cw_core/node.dart';
|
||||
import 'package:cw_core/pathForWallet.dart';
|
||||
import 'package:cw_core/pending_transaction.dart';
|
||||
import 'package:cw_core/sync_status.dart';
|
||||
import 'package:cw_core/transaction_priority.dart';
|
||||
import 'package:cw_core/unspent_coins_info.dart';
|
||||
import 'package:cw_core/wallet_base.dart';
|
||||
import 'package:cw_core/wallet_info.dart';
|
||||
import 'package:cw_monero/api/coins_info.dart';
|
||||
import 'package:cw_monero/api/monero_output.dart';
|
||||
import 'package:cw_monero/api/structs/pending_transaction.dart';
|
||||
import 'package:cw_monero/api/transaction_history.dart' as transaction_history;
|
||||
import 'package:cw_monero/api/wallet.dart' as monero_wallet;
|
||||
import 'package:cw_monero/exceptions/monero_transaction_creation_exception.dart';
|
||||
import 'package:cw_monero/exceptions/monero_transaction_no_inputs_exception.dart';
|
||||
import 'package:cw_monero/pending_monero_transaction.dart';
|
||||
import 'package:cw_monero/monero_transaction_creation_credentials.dart';
|
||||
import 'package:cw_monero/monero_transaction_history.dart';
|
||||
import 'package:cw_monero/monero_transaction_info.dart';
|
||||
import 'package:cw_monero/monero_unspent.dart';
|
||||
import 'package:cw_monero/monero_wallet_addresses.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
|
||||
part 'monero_wallet.g.dart';
|
||||
|
||||
|
@ -37,39 +39,51 @@ class MoneroWallet = MoneroWalletBase with _$MoneroWallet;
|
|||
|
||||
abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
|
||||
MoneroTransactionHistory, MoneroTransactionInfo> with Store {
|
||||
MoneroWalletBase({required WalletInfo walletInfo})
|
||||
MoneroWalletBase({required WalletInfo walletInfo,
|
||||
required Box<UnspentCoinsInfo> unspentCoinsInfo})
|
||||
: balance = ObservableMap<CryptoCurrency, MoneroBalance>.of({
|
||||
CryptoCurrency.xmr: MoneroBalance(
|
||||
CryptoCurrency.xmr: MoneroBalance(
|
||||
fullBalance: monero_wallet.getFullBalance(accountIndex: 0),
|
||||
unlockedBalance: monero_wallet.getFullBalance(accountIndex: 0))
|
||||
}),
|
||||
}),
|
||||
_isTransactionUpdating = false,
|
||||
_hasSyncAfterStartup = false,
|
||||
walletAddresses = MoneroWalletAddresses(walletInfo),
|
||||
isEnabledAutoGenerateSubaddress = false,
|
||||
syncStatus = NotConnectedSyncStatus(),
|
||||
unspentCoins = [],
|
||||
this.unspentCoinsInfo = unspentCoinsInfo,
|
||||
super(walletInfo) {
|
||||
transactionHistory = MoneroTransactionHistory();
|
||||
_onAccountChangeReaction = reaction((_) => walletAddresses.account,
|
||||
(Account? account) {
|
||||
walletAddresses = MoneroWalletAddresses(walletInfo, transactionHistory);
|
||||
|
||||
_onAccountChangeReaction = reaction((_) => walletAddresses.account, (Account? account) {
|
||||
if (account == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
balance = ObservableMap<CryptoCurrency, MoneroBalance>.of(
|
||||
<CryptoCurrency, MoneroBalance>{
|
||||
currency: MoneroBalance(
|
||||
balance = ObservableMap<CryptoCurrency, MoneroBalance>.of(<CryptoCurrency, MoneroBalance>{
|
||||
currency: MoneroBalance(
|
||||
fullBalance: monero_wallet.getFullBalance(accountIndex: account.id),
|
||||
unlockedBalance:
|
||||
monero_wallet.getUnlockedBalance(accountIndex: account.id))
|
||||
});
|
||||
walletAddresses.updateSubaddressList(accountIndex: account.id);
|
||||
unlockedBalance: monero_wallet.getUnlockedBalance(accountIndex: account.id))
|
||||
});
|
||||
_updateSubAddress(isEnabledAutoGenerateSubaddress, account: account);
|
||||
});
|
||||
|
||||
reaction((_) => isEnabledAutoGenerateSubaddress, (bool enabled) {
|
||||
_updateSubAddress(enabled, account: walletAddresses.account);
|
||||
});
|
||||
}
|
||||
|
||||
static const int _autoSaveInterval = 30;
|
||||
|
||||
Box<UnspentCoinsInfo> unspentCoinsInfo;
|
||||
|
||||
@override
|
||||
MoneroWalletAddresses walletAddresses;
|
||||
late MoneroWalletAddresses walletAddresses;
|
||||
|
||||
@override
|
||||
@observable
|
||||
bool isEnabledAutoGenerateSubaddress;
|
||||
|
||||
@override
|
||||
@observable
|
||||
|
@ -89,11 +103,12 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
|
|||
publicSpendKey: monero_wallet.getPublicSpendKey(),
|
||||
publicViewKey: monero_wallet.getPublicViewKey());
|
||||
|
||||
SyncListener? _listener;
|
||||
monero_wallet.SyncListener? _listener;
|
||||
ReactionDisposer? _onAccountChangeReaction;
|
||||
bool _isTransactionUpdating;
|
||||
bool _hasSyncAfterStartup;
|
||||
Timer? _autoSaveTimer;
|
||||
List<MoneroUnspent> unspentCoins;
|
||||
|
||||
Future<void> init() async {
|
||||
await walletAddresses.init();
|
||||
|
@ -170,10 +185,12 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
|
|||
@override
|
||||
Future<PendingTransaction> createTransaction(Object credentials) async {
|
||||
final _credentials = credentials as MoneroTransactionCreationCredentials;
|
||||
final inputs = <String>[];
|
||||
final outputs = _credentials.outputs;
|
||||
final hasMultiDestination = outputs.length > 1;
|
||||
final unlockedBalance =
|
||||
monero_wallet.getUnlockedBalance(accountIndex: walletAddresses.account!.id);
|
||||
var allInputsAmount = 0;
|
||||
|
||||
PendingTransactionDescription pendingTransactionDescription;
|
||||
|
||||
|
@ -181,6 +198,21 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
|
|||
throw MoneroTransactionCreationException('The wallet is not synced.');
|
||||
}
|
||||
|
||||
if (unspentCoins.isEmpty) {
|
||||
await updateUnspent();
|
||||
}
|
||||
|
||||
for (final utx in unspentCoins) {
|
||||
if (utx.isSending) {
|
||||
allInputsAmount += utx.value;
|
||||
inputs.add(utx.keyImage);
|
||||
}
|
||||
}
|
||||
|
||||
if (inputs.isEmpty) {
|
||||
throw MoneroTransactionNoInputsException();
|
||||
}
|
||||
|
||||
if (hasMultiDestination) {
|
||||
if (outputs.any((item) => item.sendAll
|
||||
|| (item.formattedCryptoAmount ?? 0) <= 0)) {
|
||||
|
@ -208,7 +240,8 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
|
|||
await transaction_history.createTransactionMultDest(
|
||||
outputs: moneroOutputs,
|
||||
priorityRaw: _credentials.priority.serialize(),
|
||||
accountIndex: walletAddresses.account!.id);
|
||||
accountIndex: walletAddresses.account!.id,
|
||||
preferredInputs: inputs);
|
||||
} else {
|
||||
final output = outputs.first;
|
||||
final address = output.isParsedAddress
|
||||
|
@ -229,12 +262,12 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
|
|||
'You do not have enough unlocked balance. Unlocked: $formattedBalance. Transaction amount: ${output.cryptoAmount}.');
|
||||
}
|
||||
|
||||
pendingTransactionDescription =
|
||||
await transaction_history.createTransaction(
|
||||
pendingTransactionDescription = await transaction_history.createTransaction(
|
||||
address: address!,
|
||||
amount: amount,
|
||||
priorityRaw: _credentials.priority.serialize(),
|
||||
accountIndex: walletAddresses.account!.id);
|
||||
accountIndex: walletAddresses.account!.id,
|
||||
preferredInputs: inputs);
|
||||
}
|
||||
|
||||
return PendingMoneroTransaction(pendingTransactionDescription);
|
||||
|
@ -264,6 +297,14 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
|
|||
|
||||
@override
|
||||
Future<void> save() async {
|
||||
await walletAddresses.updateUsedSubaddress();
|
||||
|
||||
if (isEnabledAutoGenerateSubaddress) {
|
||||
walletAddresses.updateUnusedSubaddress(
|
||||
accountIndex: walletAddresses.account?.id ?? 0,
|
||||
defaultLabel: walletAddresses.account?.label ?? '');
|
||||
}
|
||||
|
||||
await walletAddresses.updateAddressesInBox();
|
||||
await backupWalletFiles(name);
|
||||
await monero_wallet.store();
|
||||
|
@ -354,6 +395,85 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
|
|||
await walletInfo.save();
|
||||
}
|
||||
|
||||
Future<void> updateUnspent() async {
|
||||
refreshCoins(walletAddresses.account!.id);
|
||||
|
||||
unspentCoins.clear();
|
||||
|
||||
final coinCount = countOfCoins();
|
||||
for (var i = 0; i < coinCount; i++) {
|
||||
final coin = getCoin(i);
|
||||
if (coin.spent == 0) {
|
||||
unspentCoins.add(MoneroUnspent.fromCoinsInfoRow(coin));
|
||||
}
|
||||
}
|
||||
|
||||
if (unspentCoinsInfo.isEmpty) {
|
||||
unspentCoins.forEach((coin) => _addCoinInfo(coin));
|
||||
return;
|
||||
}
|
||||
|
||||
if (unspentCoins.isNotEmpty) {
|
||||
unspentCoins.forEach((coin) {
|
||||
final coinInfoList = unspentCoinsInfo.values.where((element) =>
|
||||
element.walletId.contains(id) && element.hash.contains(coin.hash));
|
||||
|
||||
if (coinInfoList.isNotEmpty) {
|
||||
final coinInfo = coinInfoList.first;
|
||||
|
||||
coin.isFrozen = coinInfo.isFrozen;
|
||||
coin.isSending = coinInfo.isSending;
|
||||
coin.note = coinInfo.note;
|
||||
} else {
|
||||
_addCoinInfo(coin);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
await _refreshUnspentCoinsInfo();
|
||||
_askForUpdateBalance();
|
||||
}
|
||||
|
||||
Future<void> _addCoinInfo(MoneroUnspent coin) async {
|
||||
final newInfo = UnspentCoinsInfo(
|
||||
walletId: id,
|
||||
hash: coin.hash,
|
||||
isFrozen: coin.isFrozen,
|
||||
isSending: coin.isSending,
|
||||
noteRaw: coin.note,
|
||||
address: coin.address,
|
||||
value: coin.value,
|
||||
vout: 0,
|
||||
keyImage: coin.keyImage
|
||||
);
|
||||
|
||||
await unspentCoinsInfo.add(newInfo);
|
||||
}
|
||||
|
||||
Future<void> _refreshUnspentCoinsInfo() async {
|
||||
try {
|
||||
final List<dynamic> keys = <dynamic>[];
|
||||
final currentWalletUnspentCoins = unspentCoinsInfo.values
|
||||
.where((element) => element.walletId.contains(id));
|
||||
|
||||
if (currentWalletUnspentCoins.isNotEmpty) {
|
||||
currentWalletUnspentCoins.forEach((element) {
|
||||
final existUnspentCoins = unspentCoins.where((coin) => element.hash.contains(coin.hash));
|
||||
|
||||
if (existUnspentCoins.isEmpty) {
|
||||
keys.add(element.key);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (keys.isNotEmpty) {
|
||||
await unspentCoinsInfo.deleteAll(keys);
|
||||
}
|
||||
} catch (e) {
|
||||
print(e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
String getTransactionAddress(int accountIndex, int addressIndex) =>
|
||||
monero_wallet.getAddress(
|
||||
accountIndex: accountIndex,
|
||||
|
@ -361,7 +481,7 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
|
|||
|
||||
@override
|
||||
Future<Map<String, MoneroTransactionInfo>> fetchTransactions() async {
|
||||
monero_transaction_history.refreshTransactions();
|
||||
transaction_history.refreshTransactions();
|
||||
return _getAllTransactions(null).fold<Map<String, MoneroTransactionInfo>>(
|
||||
<String, MoneroTransactionInfo>{},
|
||||
(Map<String, MoneroTransactionInfo> acc, MoneroTransactionInfo tx) {
|
||||
|
@ -392,7 +512,7 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
|
|||
}
|
||||
|
||||
List<MoneroTransactionInfo> _getAllTransactions(dynamic _) =>
|
||||
monero_transaction_history
|
||||
transaction_history
|
||||
.getAllTransations()
|
||||
.map((row) => MoneroTransactionInfo.fromRow(row))
|
||||
.toList();
|
||||
|
@ -407,7 +527,7 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
|
|||
return;
|
||||
}
|
||||
|
||||
final currentHeight = getCurrentHeight();
|
||||
final currentHeight = monero_wallet.getCurrentHeight();
|
||||
|
||||
if (currentHeight <= 1) {
|
||||
final height = _getHeightByDate(walletInfo.date);
|
||||
|
@ -439,11 +559,13 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
|
|||
void _askForUpdateBalance() {
|
||||
final unlockedBalance = _getUnlockedBalance();
|
||||
final fullBalance = _getFullBalance();
|
||||
final frozenBalance = _getFrozenBalance();
|
||||
|
||||
if (balance[currency]!.fullBalance != fullBalance ||
|
||||
balance[currency]!.unlockedBalance != unlockedBalance) {
|
||||
balance[currency]!.unlockedBalance != unlockedBalance ||
|
||||
balance[currency]!.frozenBalance != frozenBalance) {
|
||||
balance[currency] = MoneroBalance(
|
||||
fullBalance: fullBalance, unlockedBalance: unlockedBalance);
|
||||
fullBalance: fullBalance, unlockedBalance: unlockedBalance, frozenBalance: frozenBalance);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -456,6 +578,17 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
|
|||
int _getUnlockedBalance() =>
|
||||
monero_wallet.getUnlockedBalance(accountIndex: walletAddresses.account!.id);
|
||||
|
||||
int _getFrozenBalance() {
|
||||
var frozenBalance = 0;
|
||||
|
||||
for (var coin in unspentCoinsInfo.values) {
|
||||
if (coin.isFrozen)
|
||||
frozenBalance += coin.value;
|
||||
}
|
||||
|
||||
return frozenBalance;
|
||||
}
|
||||
|
||||
void _onNewBlock(int height, int blocksLeft, double ptc) async {
|
||||
try {
|
||||
if (walletInfo.isRecovery) {
|
||||
|
@ -495,4 +628,15 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
|
|||
print(e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
void _updateSubAddress(bool enableAutoGenerate, {Account? account}) {
|
||||
if (enableAutoGenerate) {
|
||||
walletAddresses.updateUnusedSubaddress(
|
||||
accountIndex: account?.id ?? 0,
|
||||
defaultLabel: account?.label ?? '',
|
||||
);
|
||||
} else {
|
||||
walletAddresses.updateSubaddressList(accountIndex: account?.id ?? 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,27 +1,32 @@
|
|||
import 'package:cw_core/address_info.dart';
|
||||
import 'package:cw_core/wallet_addresses.dart';
|
||||
import 'package:cw_core/wallet_info.dart';
|
||||
import 'package:cw_core/account.dart';
|
||||
import 'package:cw_monero/api/wallet.dart';
|
||||
import 'package:cw_monero/monero_account_list.dart';
|
||||
import 'package:cw_monero/monero_subaddress_list.dart';
|
||||
import 'package:cw_core/subaddress.dart';
|
||||
import 'package:cw_monero/monero_transaction_history.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
|
||||
part 'monero_wallet_addresses.g.dart';
|
||||
|
||||
class MoneroWalletAddresses = MoneroWalletAddressesBase
|
||||
with _$MoneroWalletAddresses;
|
||||
class MoneroWalletAddresses = MoneroWalletAddressesBase with _$MoneroWalletAddresses;
|
||||
|
||||
abstract class MoneroWalletAddressesBase extends WalletAddresses with Store {
|
||||
MoneroWalletAddressesBase(WalletInfo walletInfo)
|
||||
: accountList = MoneroAccountList(),
|
||||
subaddressList = MoneroSubaddressList(),
|
||||
address = '',
|
||||
super(walletInfo);
|
||||
MoneroWalletAddressesBase(
|
||||
WalletInfo walletInfo, MoneroTransactionHistory moneroTransactionHistory)
|
||||
: accountList = MoneroAccountList(),
|
||||
_moneroTransactionHistory = moneroTransactionHistory,
|
||||
subaddressList = MoneroSubaddressList(),
|
||||
address = '',
|
||||
super(walletInfo);
|
||||
|
||||
final MoneroTransactionHistory _moneroTransactionHistory;
|
||||
@override
|
||||
@observable
|
||||
String address;
|
||||
|
||||
|
||||
@observable
|
||||
Account? account;
|
||||
|
||||
|
@ -46,11 +51,15 @@ abstract class MoneroWalletAddressesBase extends WalletAddresses with Store {
|
|||
final _subaddressList = MoneroSubaddressList();
|
||||
|
||||
addressesMap.clear();
|
||||
addressInfos.clear();
|
||||
|
||||
accountList.accounts.forEach((account) {
|
||||
_subaddressList.update(accountIndex: account.id);
|
||||
_subaddressList.subaddresses.forEach((subaddress) {
|
||||
addressesMap[subaddress.address] = subaddress.label;
|
||||
addressInfos[account.id] ??= [];
|
||||
addressInfos[account.id]?.add(AddressInfo(
|
||||
address: subaddress.address, label: subaddress.label, accountIndex: account.id));
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -62,14 +71,14 @@ abstract class MoneroWalletAddressesBase extends WalletAddresses with Store {
|
|||
|
||||
bool validate() {
|
||||
accountList.update();
|
||||
final accountListLength = accountList.accounts.length ?? 0;
|
||||
final accountListLength = accountList.accounts.length;
|
||||
|
||||
if (accountListLength <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
subaddressList.update(accountIndex: accountList.accounts.first.id);
|
||||
final subaddressListLength = subaddressList.subaddresses.length ?? 0;
|
||||
final subaddressListLength = subaddressList.subaddresses.length;
|
||||
|
||||
if (subaddressListLength <= 0) {
|
||||
return false;
|
||||
|
@ -83,4 +92,24 @@ abstract class MoneroWalletAddressesBase extends WalletAddresses with Store {
|
|||
subaddress = subaddressList.subaddresses.first;
|
||||
address = subaddress!.address;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> updateUsedSubaddress() async {
|
||||
final transactions = _moneroTransactionHistory.transactions.values.toList();
|
||||
|
||||
transactions.forEach((element) {
|
||||
final accountIndex = element.accountIndex;
|
||||
final addressIndex = element.addressIndex;
|
||||
usedAddresses.add(getAddress(accountIndex: accountIndex, addressIndex: addressIndex));
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> updateUnusedSubaddress(
|
||||
{required int accountIndex, required String defaultLabel}) async {
|
||||
await subaddressList.updateWithAutoGenerate(
|
||||
accountIndex: accountIndex,
|
||||
defaultLabel: defaultLabel,
|
||||
usedAddresses: usedAddresses.toList());
|
||||
subaddress = subaddressList.subaddresses.last;
|
||||
address = subaddress!.address;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
import 'dart:io';
|
||||
import 'package:cw_core/wallet_base.dart';
|
||||
import 'package:cw_core/monero_wallet_utils.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:cw_monero/api/wallet_manager.dart' as monero_wallet_manager;
|
||||
import 'package:cw_monero/api/wallet.dart' as monero_wallet;
|
||||
import 'package:cw_monero/api/exceptions/wallet_opening_exception.dart';
|
||||
import 'package:cw_monero/monero_wallet.dart';
|
||||
import 'package:cw_core/wallet_credentials.dart';
|
||||
import 'package:cw_core/wallet_service.dart';
|
||||
import 'package:cw_core/pathForWallet.dart';
|
||||
import 'package:cw_core/unspent_coins_info.dart';
|
||||
import 'package:cw_core/wallet_base.dart';
|
||||
import 'package:cw_core/wallet_credentials.dart';
|
||||
import 'package:cw_core/wallet_info.dart';
|
||||
import 'package:cw_core/wallet_service.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:cw_monero/api/exceptions/wallet_opening_exception.dart';
|
||||
import 'package:cw_monero/api/wallet_manager.dart' as monero_wallet_manager;
|
||||
import 'package:cw_monero/monero_wallet.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
|
||||
class MoneroNewWalletCredentials extends WalletCredentials {
|
||||
MoneroNewWalletCredentials({required String name, required this.language, String? password})
|
||||
|
@ -53,10 +53,11 @@ class MoneroWalletService extends WalletService<
|
|||
MoneroNewWalletCredentials,
|
||||
MoneroRestoreWalletFromSeedCredentials,
|
||||
MoneroRestoreWalletFromKeysCredentials> {
|
||||
MoneroWalletService(this.walletInfoSource);
|
||||
MoneroWalletService(this.walletInfoSource, this.unspentCoinsInfoSource);
|
||||
|
||||
final Box<WalletInfo> walletInfoSource;
|
||||
|
||||
final Box<UnspentCoinsInfo> unspentCoinsInfoSource;
|
||||
|
||||
static bool walletFilesExist(String path) =>
|
||||
!File(path).existsSync() && !File('$path.keys').existsSync();
|
||||
|
||||
|
@ -71,7 +72,8 @@ class MoneroWalletService extends WalletService<
|
|||
path: path,
|
||||
password: credentials.password!,
|
||||
language: credentials.language);
|
||||
final wallet = MoneroWallet(walletInfo: credentials.walletInfo!);
|
||||
final wallet = MoneroWallet(
|
||||
walletInfo: credentials.walletInfo!, unspentCoinsInfo: unspentCoinsInfoSource);
|
||||
await wallet.init();
|
||||
|
||||
return wallet;
|
||||
|
@ -107,7 +109,7 @@ class MoneroWalletService extends WalletService<
|
|||
.openWalletAsync({'path': path, 'password': password});
|
||||
final walletInfo = walletInfoSource.values.firstWhere(
|
||||
(info) => info.id == WalletBase.idFor(name, getType()));
|
||||
final wallet = MoneroWallet(walletInfo: walletInfo);
|
||||
final wallet = MoneroWallet(walletInfo: walletInfo, unspentCoinsInfo: unspentCoinsInfoSource);
|
||||
final isValid = wallet.walletAddresses.validate();
|
||||
|
||||
if (!isValid) {
|
||||
|
@ -122,13 +124,20 @@ class MoneroWalletService extends WalletService<
|
|||
} catch (e) {
|
||||
// TODO: Implement Exception for wallet list service.
|
||||
|
||||
if ((e.toString().contains('bad_alloc') ||
|
||||
final bool isBadAlloc = e.toString().contains('bad_alloc') ||
|
||||
(e is WalletOpeningException &&
|
||||
(e.message == 'std::bad_alloc' ||
|
||||
e.message.contains('bad_alloc')))) ||
|
||||
(e.toString().contains('does not correspond') ||
|
||||
(e is WalletOpeningException &&
|
||||
e.message.contains('does not correspond')))) {
|
||||
(e.message == 'std::bad_alloc' || e.message.contains('bad_alloc')));
|
||||
|
||||
final bool doesNotCorrespond = e.toString().contains('does not correspond') ||
|
||||
(e is WalletOpeningException && e.message.contains('does not correspond'));
|
||||
|
||||
final bool isMissingCacheFilesIOS = e.toString().contains('basic_string') ||
|
||||
(e is WalletOpeningException && e.message.contains('basic_string'));
|
||||
|
||||
final bool isMissingCacheFilesAndroid = e.toString().contains('input_stream') ||
|
||||
(e is WalletOpeningException && e.message.contains('input_stream'));
|
||||
|
||||
if (isBadAlloc || doesNotCorrespond || isMissingCacheFilesIOS || isMissingCacheFilesAndroid) {
|
||||
await restoreOrResetWalletFiles(name);
|
||||
return openWallet(name, password);
|
||||
}
|
||||
|
@ -157,7 +166,8 @@ class MoneroWalletService extends WalletService<
|
|||
String currentName, String password, String newName) async {
|
||||
final currentWalletInfo = walletInfoSource.values.firstWhere(
|
||||
(info) => info.id == WalletBase.idFor(currentName, getType()));
|
||||
final currentWallet = MoneroWallet(walletInfo: currentWalletInfo);
|
||||
final currentWallet =
|
||||
MoneroWallet(walletInfo: currentWalletInfo, unspentCoinsInfo: unspentCoinsInfoSource);
|
||||
|
||||
await currentWallet.renameWalletFiles(newName);
|
||||
|
||||
|
@ -181,7 +191,8 @@ class MoneroWalletService extends WalletService<
|
|||
address: credentials.address,
|
||||
viewKey: credentials.viewKey,
|
||||
spendKey: credentials.spendKey);
|
||||
final wallet = MoneroWallet(walletInfo: credentials.walletInfo!);
|
||||
final wallet = MoneroWallet(
|
||||
walletInfo: credentials.walletInfo!, unspentCoinsInfo: unspentCoinsInfoSource);
|
||||
await wallet.init();
|
||||
|
||||
return wallet;
|
||||
|
@ -202,7 +213,8 @@ class MoneroWalletService extends WalletService<
|
|||
password: credentials.password!,
|
||||
seed: credentials.mnemonic,
|
||||
restoreHeight: credentials.height!);
|
||||
final wallet = MoneroWallet(walletInfo: credentials.walletInfo!);
|
||||
final wallet = MoneroWallet(
|
||||
walletInfo: credentials.walletInfo!, unspentCoinsInfo: unspentCoinsInfoSource);
|
||||
await wallet.init();
|
||||
|
||||
return wallet;
|
||||
|
|
|
@ -3,8 +3,10 @@
|
|||
#include <chrono>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <unistd.h>
|
||||
#include <mutex>
|
||||
#include <list>
|
||||
#include "thread"
|
||||
#include "CwWalletListener.h"
|
||||
#if __APPLE__
|
||||
|
@ -137,7 +139,7 @@ extern "C"
|
|||
int8_t direction;
|
||||
int8_t isPending;
|
||||
uint32_t subaddrIndex;
|
||||
|
||||
|
||||
char *hash;
|
||||
char *paymentId;
|
||||
|
||||
|
@ -152,7 +154,7 @@ extern "C"
|
|||
std::set<uint32_t>::iterator it = transaction->subaddrIndex().begin();
|
||||
subaddrIndex = *it;
|
||||
confirmations = transaction->confirmations();
|
||||
datetime = static_cast<int64_t>(transaction->timestamp());
|
||||
datetime = static_cast<int64_t>(transaction->timestamp());
|
||||
direction = transaction->direction();
|
||||
isPending = static_cast<int8_t>(transaction->isPending());
|
||||
std::string *hash_str = new std::string(transaction->hash());
|
||||
|
@ -181,6 +183,62 @@ extern "C"
|
|||
}
|
||||
};
|
||||
|
||||
struct CoinsInfoRow
|
||||
{
|
||||
uint64_t blockHeight;
|
||||
char *hash;
|
||||
uint64_t internalOutputIndex;
|
||||
uint64_t globalOutputIndex;
|
||||
bool spent;
|
||||
bool frozen;
|
||||
uint64_t spentHeight;
|
||||
uint64_t amount;
|
||||
bool rct;
|
||||
bool keyImageKnown;
|
||||
uint64_t pkIndex;
|
||||
uint32_t subaddrIndex;
|
||||
uint32_t subaddrAccount;
|
||||
char *address;
|
||||
char *addressLabel;
|
||||
char *keyImage;
|
||||
uint64_t unlockTime;
|
||||
bool unlocked;
|
||||
char *pubKey;
|
||||
bool coinbase;
|
||||
char *description;
|
||||
|
||||
CoinsInfoRow(Monero::CoinsInfo *coinsInfo)
|
||||
{
|
||||
blockHeight = coinsInfo->blockHeight();
|
||||
std::string *hash_str = new std::string(coinsInfo->hash());
|
||||
hash = strdup(hash_str->c_str());
|
||||
internalOutputIndex = coinsInfo->internalOutputIndex();
|
||||
globalOutputIndex = coinsInfo->globalOutputIndex();
|
||||
spent = coinsInfo->spent();
|
||||
frozen = coinsInfo->frozen();
|
||||
spentHeight = coinsInfo->spentHeight();
|
||||
amount = coinsInfo->amount();
|
||||
rct = coinsInfo->rct();
|
||||
keyImageKnown = coinsInfo->keyImageKnown();
|
||||
pkIndex = coinsInfo->pkIndex();
|
||||
subaddrIndex = coinsInfo->subaddrIndex();
|
||||
subaddrAccount = coinsInfo->subaddrAccount();
|
||||
address = strdup(coinsInfo->address().c_str()) ;
|
||||
addressLabel = strdup(coinsInfo->addressLabel().c_str());
|
||||
keyImage = strdup(coinsInfo->keyImage().c_str());
|
||||
unlockTime = coinsInfo->unlockTime();
|
||||
unlocked = coinsInfo->unlocked();
|
||||
pubKey = strdup(coinsInfo->pubKey().c_str());
|
||||
coinbase = coinsInfo->coinbase();
|
||||
description = strdup(coinsInfo->description().c_str());
|
||||
}
|
||||
|
||||
void setUnlocked(bool unlocked);
|
||||
|
||||
};
|
||||
|
||||
Monero::Coins *m_coins;
|
||||
|
||||
Monero::Wallet *m_wallet;
|
||||
Monero::TransactionHistory *m_transaction_history;
|
||||
MoneroWalletListener *m_listener;
|
||||
|
@ -188,6 +246,7 @@ extern "C"
|
|||
Monero::SubaddressAccount *m_account;
|
||||
uint64_t m_last_known_wallet_height;
|
||||
uint64_t m_cached_syncing_blockchain_height = 0;
|
||||
std::list<Monero::CoinsInfo*> m_coins_info;
|
||||
std::mutex store_lock;
|
||||
bool is_storing = false;
|
||||
|
||||
|
@ -195,7 +254,7 @@ extern "C"
|
|||
{
|
||||
m_wallet = wallet;
|
||||
m_listener = nullptr;
|
||||
|
||||
|
||||
|
||||
if (wallet != nullptr)
|
||||
{
|
||||
|
@ -223,6 +282,17 @@ extern "C"
|
|||
{
|
||||
m_subaddress = nullptr;
|
||||
}
|
||||
|
||||
m_coins_info = std::list<Monero::CoinsInfo*>();
|
||||
|
||||
if (wallet != nullptr)
|
||||
{
|
||||
m_coins = wallet->coins();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_coins = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
Monero::Wallet *get_current_wallet()
|
||||
|
@ -405,13 +475,14 @@ extern "C"
|
|||
return is_connected;
|
||||
}
|
||||
|
||||
bool setup_node(char *address, char *login, char *password, bool use_ssl, bool is_light_wallet, char *error)
|
||||
bool setup_node(char *address, char *login, char *password, bool use_ssl, bool is_light_wallet, char *socksProxyAddress, char *error)
|
||||
{
|
||||
nice(19);
|
||||
Monero::Wallet *wallet = get_current_wallet();
|
||||
|
||||
|
||||
std::string _login = "";
|
||||
std::string _password = "";
|
||||
std::string _socksProxyAddress = "";
|
||||
|
||||
if (login != nullptr)
|
||||
{
|
||||
|
@ -423,7 +494,12 @@ extern "C"
|
|||
_password = std::string(password);
|
||||
}
|
||||
|
||||
bool inited = wallet->init(std::string(address), 0, _login, _password, use_ssl, is_light_wallet);
|
||||
if (socksProxyAddress != nullptr)
|
||||
{
|
||||
_socksProxyAddress = std::string(socksProxyAddress);
|
||||
}
|
||||
|
||||
bool inited = wallet->init(std::string(address), 0, _login, _password, use_ssl, is_light_wallet, _socksProxyAddress);
|
||||
|
||||
if (!inited)
|
||||
{
|
||||
|
@ -480,10 +556,19 @@ extern "C"
|
|||
}
|
||||
|
||||
bool transaction_create(char *address, char *payment_id, char *amount,
|
||||
uint8_t priority_raw, uint32_t subaddr_account, Utf8Box &error, PendingTransactionRaw &pendingTransaction)
|
||||
uint8_t priority_raw, uint32_t subaddr_account,
|
||||
char **preferred_inputs, uint32_t preferred_inputs_size,
|
||||
Utf8Box &error, PendingTransactionRaw &pendingTransaction)
|
||||
{
|
||||
nice(19);
|
||||
|
||||
|
||||
std::set<std::string> _preferred_inputs;
|
||||
|
||||
for (int i = 0; i < preferred_inputs_size; i++) {
|
||||
_preferred_inputs.insert(std::string(*preferred_inputs));
|
||||
preferred_inputs++;
|
||||
}
|
||||
|
||||
auto priority = static_cast<Monero::PendingTransaction::Priority>(priority_raw);
|
||||
std::string _payment_id;
|
||||
Monero::PendingTransaction *transaction;
|
||||
|
@ -496,13 +581,13 @@ extern "C"
|
|||
if (amount != nullptr)
|
||||
{
|
||||
uint64_t _amount = Monero::Wallet::amountFromString(std::string(amount));
|
||||
transaction = m_wallet->createTransaction(std::string(address), _payment_id, _amount, m_wallet->defaultMixin(), priority, subaddr_account);
|
||||
transaction = m_wallet->createTransaction(std::string(address), _payment_id, _amount, m_wallet->defaultMixin(), priority, subaddr_account, {}, _preferred_inputs);
|
||||
}
|
||||
else
|
||||
{
|
||||
transaction = m_wallet->createTransaction(std::string(address), _payment_id, Monero::optional<uint64_t>(), m_wallet->defaultMixin(), priority, subaddr_account);
|
||||
transaction = m_wallet->createTransaction(std::string(address), _payment_id, Monero::optional<uint64_t>(), m_wallet->defaultMixin(), priority, subaddr_account, {}, _preferred_inputs);
|
||||
}
|
||||
|
||||
|
||||
int status = transaction->status();
|
||||
|
||||
if (status == Monero::PendingTransaction::Status::Status_Error || status == Monero::PendingTransaction::Status::Status_Critical)
|
||||
|
@ -520,7 +605,9 @@ extern "C"
|
|||
}
|
||||
|
||||
bool transaction_create_mult_dest(char **addresses, char *payment_id, char **amounts, uint32_t size,
|
||||
uint8_t priority_raw, uint32_t subaddr_account, Utf8Box &error, PendingTransactionRaw &pendingTransaction)
|
||||
uint8_t priority_raw, uint32_t subaddr_account,
|
||||
char **preferred_inputs, uint32_t preferred_inputs_size,
|
||||
Utf8Box &error, PendingTransactionRaw &pendingTransaction)
|
||||
{
|
||||
nice(19);
|
||||
|
||||
|
@ -534,6 +621,13 @@ extern "C"
|
|||
amounts++;
|
||||
}
|
||||
|
||||
std::set<std::string> _preferred_inputs;
|
||||
|
||||
for (int i = 0; i < preferred_inputs_size; i++) {
|
||||
_preferred_inputs.insert(std::string(*preferred_inputs));
|
||||
preferred_inputs++;
|
||||
}
|
||||
|
||||
auto priority = static_cast<Monero::PendingTransaction::Priority>(priority_raw);
|
||||
std::string _payment_id;
|
||||
Monero::PendingTransaction *transaction;
|
||||
|
@ -793,6 +887,91 @@ extern "C"
|
|||
return m_wallet->trustedDaemon();
|
||||
}
|
||||
|
||||
CoinsInfoRow* coin(int index)
|
||||
{
|
||||
if (index >= 0 && index < m_coins_info.size()) {
|
||||
std::list<Monero::CoinsInfo*>::iterator it = m_coins_info.begin();
|
||||
std::advance(it, index);
|
||||
Monero::CoinsInfo* element = *it;
|
||||
std::cout << "Element at index " << index << ": " << element << std::endl;
|
||||
return new CoinsInfoRow(element);
|
||||
} else {
|
||||
std::cout << "Invalid index." << std::endl;
|
||||
return nullptr; // Return a default value (nullptr) for invalid index
|
||||
}
|
||||
}
|
||||
|
||||
void refresh_coins(uint32_t accountIndex)
|
||||
{
|
||||
m_coins_info.clear();
|
||||
|
||||
m_coins->refresh();
|
||||
for (const auto i : m_coins->getAll()) {
|
||||
if (i->subaddrAccount() == accountIndex && !(i->spent())) {
|
||||
m_coins_info.push_back(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t coins_count()
|
||||
{
|
||||
return m_coins_info.size();
|
||||
}
|
||||
|
||||
CoinsInfoRow** coins_from_account(uint32_t accountIndex)
|
||||
{
|
||||
std::vector<CoinsInfoRow*> matchingCoins;
|
||||
|
||||
for (int i = 0; i < coins_count(); i++) {
|
||||
CoinsInfoRow* coinInfo = coin(i);
|
||||
if (coinInfo->subaddrAccount == accountIndex) {
|
||||
matchingCoins.push_back(coinInfo);
|
||||
}
|
||||
}
|
||||
|
||||
CoinsInfoRow** result = new CoinsInfoRow*[matchingCoins.size()];
|
||||
std::copy(matchingCoins.begin(), matchingCoins.end(), result);
|
||||
return result;
|
||||
}
|
||||
|
||||
CoinsInfoRow** coins_from_txid(const char* txid, size_t* count)
|
||||
{
|
||||
std::vector<CoinsInfoRow*> matchingCoins;
|
||||
|
||||
for (int i = 0; i < coins_count(); i++) {
|
||||
CoinsInfoRow* coinInfo = coin(i);
|
||||
if (std::string(coinInfo->hash) == txid) {
|
||||
matchingCoins.push_back(coinInfo);
|
||||
}
|
||||
}
|
||||
|
||||
*count = matchingCoins.size();
|
||||
CoinsInfoRow** result = new CoinsInfoRow*[*count];
|
||||
std::copy(matchingCoins.begin(), matchingCoins.end(), result);
|
||||
return result;
|
||||
}
|
||||
|
||||
CoinsInfoRow** coins_from_key_image(const char** keyimages, size_t keyimageCount, size_t* count)
|
||||
{
|
||||
std::vector<CoinsInfoRow*> matchingCoins;
|
||||
|
||||
for (int i = 0; i < coins_count(); i++) {
|
||||
CoinsInfoRow* coinsInfoRow = coin(i);
|
||||
for (size_t j = 0; j < keyimageCount; j++) {
|
||||
if (coinsInfoRow->keyImageKnown && std::string(coinsInfoRow->keyImage) == keyimages[j]) {
|
||||
matchingCoins.push_back(coinsInfoRow);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*count = matchingCoins.size();
|
||||
CoinsInfoRow** result = new CoinsInfoRow*[*count];
|
||||
std::copy(matchingCoins.begin(), matchingCoins.end(), result);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -174,12 +174,12 @@ DEPENDENCIES:
|
|||
- in_app_review (from `.symlinks/plugins/in_app_review/ios`)
|
||||
- local_auth_ios (from `.symlinks/plugins/local_auth_ios/ios`)
|
||||
- package_info (from `.symlinks/plugins/package_info/ios`)
|
||||
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/ios`)
|
||||
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
|
||||
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
|
||||
- platform_device_id (from `.symlinks/plugins/platform_device_id/ios`)
|
||||
- sensitive_clipboard (from `.symlinks/plugins/sensitive_clipboard/ios`)
|
||||
- share_plus (from `.symlinks/plugins/share_plus/ios`)
|
||||
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/ios`)
|
||||
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
|
||||
- uni_links (from `.symlinks/plugins/uni_links/ios`)
|
||||
- UnstoppableDomainsResolution (~> 4.0.0)
|
||||
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
|
||||
|
@ -236,7 +236,7 @@ EXTERNAL SOURCES:
|
|||
package_info:
|
||||
:path: ".symlinks/plugins/package_info/ios"
|
||||
path_provider_foundation:
|
||||
:path: ".symlinks/plugins/path_provider_foundation/ios"
|
||||
:path: ".symlinks/plugins/path_provider_foundation/darwin"
|
||||
permission_handler_apple:
|
||||
:path: ".symlinks/plugins/permission_handler_apple/ios"
|
||||
platform_device_id:
|
||||
|
@ -246,7 +246,7 @@ EXTERNAL SOURCES:
|
|||
share_plus:
|
||||
:path: ".symlinks/plugins/share_plus/ios"
|
||||
shared_preferences_foundation:
|
||||
:path: ".symlinks/plugins/shared_preferences_foundation/ios"
|
||||
:path: ".symlinks/plugins/shared_preferences_foundation/darwin"
|
||||
uni_links:
|
||||
:path: ".symlinks/plugins/uni_links/ios"
|
||||
url_launcher_ios:
|
||||
|
|
|
@ -128,7 +128,8 @@ class CWBitcoin extends Bitcoin {
|
|||
bitcoinUnspent.address.address,
|
||||
bitcoinUnspent.hash,
|
||||
bitcoinUnspent.value,
|
||||
bitcoinUnspent.vout))
|
||||
bitcoinUnspent.vout,
|
||||
null))
|
||||
.toList();
|
||||
}
|
||||
|
||||
|
@ -160,4 +161,4 @@ class CWBitcoin extends Bitcoin {
|
|||
@override
|
||||
TransactionPriority getLitecoinTransactionPrioritySlow()
|
||||
=> LitecoinTransactionPriority.slow;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
import 'package:cake_wallet/.secrets.g.dart' as secrets;
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/store/settings_store.dart';
|
||||
import 'package:cake_wallet/themes/theme_base.dart';
|
||||
import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/wallet_base.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class OnRamperBuyProvider {
|
||||
OnRamperBuyProvider({required SettingsStore settingsStore, required WalletBase wallet})
|
||||
|
@ -27,7 +29,11 @@ class OnRamperBuyProvider {
|
|||
}
|
||||
}
|
||||
|
||||
Uri requestUrl() {
|
||||
String getColorStr(Color color) {
|
||||
return color.value.toRadixString(16).replaceAll(RegExp(r'^ff'), "");
|
||||
}
|
||||
|
||||
Uri requestUrl(BuildContext context) {
|
||||
String primaryColor,
|
||||
secondaryColor,
|
||||
primaryTextColor,
|
||||
|
@ -35,31 +41,16 @@ class OnRamperBuyProvider {
|
|||
containerColor,
|
||||
cardColor;
|
||||
|
||||
switch (_settingsStore.currentTheme.type) {
|
||||
case ThemeType.bright:
|
||||
primaryColor = '815dfbff';
|
||||
secondaryColor = 'ffffff';
|
||||
primaryTextColor = '141519';
|
||||
secondaryTextColor = '6b6f80';
|
||||
containerColor = 'ffffff';
|
||||
cardColor = 'f2f0faff';
|
||||
break;
|
||||
case ThemeType.light:
|
||||
primaryColor = '2194ffff';
|
||||
secondaryColor = 'ffffff';
|
||||
primaryTextColor = '141519';
|
||||
secondaryTextColor = '6b6f80';
|
||||
containerColor = 'ffffff';
|
||||
cardColor = 'e5f7ff';
|
||||
break;
|
||||
case ThemeType.dark:
|
||||
primaryColor = '456effff';
|
||||
secondaryColor = '1b2747ff';
|
||||
primaryTextColor = 'ffffff';
|
||||
secondaryTextColor = 'ffffff';
|
||||
containerColor = '19233C';
|
||||
cardColor = '232f4fff';
|
||||
break;
|
||||
primaryColor = getColorStr(Theme.of(context).primaryColor);
|
||||
secondaryColor = getColorStr(Theme.of(context).colorScheme.background);
|
||||
primaryTextColor = getColorStr(Theme.of(context).extension<CakeTextTheme>()!.titleColor);
|
||||
secondaryTextColor =
|
||||
getColorStr(Theme.of(context).extension<CakeTextTheme>()!.secondaryTextColor);
|
||||
containerColor = getColorStr(Theme.of(context).colorScheme.background);
|
||||
cardColor = getColorStr(Theme.of(context).cardColor);
|
||||
|
||||
if (_settingsStore.currentTheme.title == S.current.high_contrast_theme) {
|
||||
cardColor = getColorStr(Colors.white);
|
||||
}
|
||||
|
||||
final networkName = _wallet.currency.fullName?.toUpperCase().replaceAll(" ", "");
|
||||
|
|
|
@ -246,6 +246,7 @@ class BackupService {
|
|||
final useEtherscan = data[PreferencesKey.useEtherscan] as bool?;
|
||||
final syncAll = data[PreferencesKey.syncAllKey] as bool?;
|
||||
final syncMode = data[PreferencesKey.syncModeKey] as int?;
|
||||
final autoGenerateSubaddressStatus = data[PreferencesKey.autoGenerateSubaddressStatusKey] as int?;
|
||||
|
||||
await _sharedPreferences.setString(PreferencesKey.currentWalletName, currentWalletName);
|
||||
|
||||
|
@ -296,6 +297,9 @@ class BackupService {
|
|||
|
||||
if (fiatApiMode != null)
|
||||
await _sharedPreferences.setInt(PreferencesKey.currentFiatApiModeKey, fiatApiMode);
|
||||
if (autoGenerateSubaddressStatus != null)
|
||||
await _sharedPreferences.setInt(PreferencesKey.autoGenerateSubaddressStatusKey,
|
||||
autoGenerateSubaddressStatus);
|
||||
|
||||
if (currentPinLength != null)
|
||||
await _sharedPreferences.setInt(PreferencesKey.currentPinLength, currentPinLength);
|
||||
|
@ -523,6 +527,8 @@ class BackupService {
|
|||
_sharedPreferences.getInt(PreferencesKey.syncModeKey),
|
||||
PreferencesKey.syncAllKey:
|
||||
_sharedPreferences.getBool(PreferencesKey.syncAllKey),
|
||||
PreferencesKey.autoGenerateSubaddressStatusKey:
|
||||
_sharedPreferences.getInt(PreferencesKey.autoGenerateSubaddressStatusKey),
|
||||
};
|
||||
|
||||
return json.encode(preferences);
|
||||
|
|
|
@ -21,7 +21,7 @@ Future<double> _fetchPrice(Map<String, dynamic> args) async {
|
|||
'key': secrets.fiatApiKey,
|
||||
};
|
||||
|
||||
double price = 0.0;
|
||||
num price = 0.0;
|
||||
|
||||
try {
|
||||
late final Uri uri;
|
||||
|
@ -41,12 +41,12 @@ Future<double> _fetchPrice(Map<String, dynamic> args) async {
|
|||
final results = responseJSON['results'] as Map<String, dynamic>;
|
||||
|
||||
if (results.isNotEmpty) {
|
||||
price = results.values.first as double;
|
||||
price = results.values.first as num;
|
||||
}
|
||||
|
||||
return price;
|
||||
return price.toDouble();
|
||||
} catch (e) {
|
||||
return price;
|
||||
return price.toDouble();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
30
lib/core/wallet_change_listener_view_model.dart
Normal file
30
lib/core/wallet_change_listener_view_model.dart
Normal file
|
@ -0,0 +1,30 @@
|
|||
import 'package:cw_core/balance.dart';
|
||||
import 'package:cw_core/transaction_history.dart';
|
||||
import 'package:cw_core/transaction_info.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:cw_core/wallet_base.dart';
|
||||
import 'package:cake_wallet/store/app_store.dart';
|
||||
|
||||
part 'wallet_change_listener_view_model.g.dart';
|
||||
|
||||
class WalletChangeListenerViewModel = WalletChangeListenerViewModelBase
|
||||
with _$WalletChangeListenerViewModel;
|
||||
|
||||
abstract class WalletChangeListenerViewModelBase with Store {
|
||||
WalletChangeListenerViewModelBase({
|
||||
required AppStore appStore,
|
||||
}) : _wallet = appStore.wallet! {
|
||||
reaction((_) => appStore.wallet, (WalletBase? wallet) {
|
||||
_wallet = wallet!;
|
||||
onWalletChange(wallet);
|
||||
});
|
||||
}
|
||||
|
||||
void onWalletChange(WalletBase wallet) {}
|
||||
|
||||
@observable
|
||||
WalletBase<Balance, TransactionHistoryBase<TransactionInfo>, TransactionInfo> _wallet;
|
||||
@computed
|
||||
WalletBase<Balance, TransactionHistoryBase<TransactionInfo>, TransactionInfo> get wallet =>
|
||||
_wallet;
|
||||
}
|
40
lib/di.dart
40
lib/di.dart
|
@ -5,6 +5,7 @@ import 'package:cake_wallet/buy/onramper/onramper_buy_provider.dart';
|
|||
import 'package:cake_wallet/buy/payfura/payfura_buy_provider.dart';
|
||||
import 'package:cake_wallet/core/yat_service.dart';
|
||||
import 'package:cake_wallet/entities/background_tasks.dart';
|
||||
import 'package:cake_wallet/entities/auto_generate_subaddress_status.dart';
|
||||
import 'package:cake_wallet/entities/exchange_api_mode.dart';
|
||||
import 'package:cake_wallet/entities/parse_address_from_domain.dart';
|
||||
import 'package:cake_wallet/entities/receive_page_option.dart';
|
||||
|
@ -177,8 +178,6 @@ import 'package:hive/hive.dart';
|
|||
import 'package:mobx/mobx.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_restoration_from_seed_vm.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_restoration_from_keys_vm.dart';
|
||||
import 'package:cake_wallet/core/wallet_creation_service.dart';
|
||||
import 'package:cake_wallet/store/app_store.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
|
@ -218,10 +217,10 @@ late Box<Template> _templates;
|
|||
late Box<ExchangeTemplate> _exchangeTemplates;
|
||||
late Box<TransactionDescription> _transactionDescriptionBox;
|
||||
late Box<Order> _ordersSource;
|
||||
late Box<UnspentCoinsInfo>? _unspentCoinsInfoSource;
|
||||
late Box<UnspentCoinsInfo> _unspentCoinsInfoSource;
|
||||
late Box<AnonpayInvoiceInfo> _anonpayInvoiceInfoSource;
|
||||
|
||||
Future setup({
|
||||
Future<void> setup({
|
||||
required Box<WalletInfo> walletInfoSource,
|
||||
required Box<Node> nodeSource,
|
||||
required Box<Contact> contactSource,
|
||||
|
@ -230,7 +229,7 @@ Future setup({
|
|||
required Box<ExchangeTemplate> exchangeTemplates,
|
||||
required Box<TransactionDescription> transactionDescriptionBox,
|
||||
required Box<Order> ordersSource,
|
||||
Box<UnspentCoinsInfo>? unspentCoinsInfoSource,
|
||||
required Box<UnspentCoinsInfo> unspentCoinsInfoSource,
|
||||
required Box<AnonpayInvoiceInfo> anonpayInvoiceInfoSource,
|
||||
}) async {
|
||||
_walletInfoSource = walletInfoSource;
|
||||
|
@ -247,7 +246,6 @@ Future setup({
|
|||
if (!_isSetupFinished) {
|
||||
getIt.registerSingletonAsync<SharedPreferences>(() => SharedPreferences.getInstance());
|
||||
}
|
||||
|
||||
if (!_isSetupFinished) {
|
||||
getIt.registerFactory(() => BackgroundTasks());
|
||||
}
|
||||
|
@ -320,25 +318,6 @@ Future setup({
|
|||
getIt.get<AppStore>(), getIt.get<WalletCreationService>(param1: type), _walletInfoSource,
|
||||
type: type));
|
||||
|
||||
getIt.registerFactoryParam<WalletRestorationFromSeedVM, List, void>((args, _) {
|
||||
final type = args.first as WalletType;
|
||||
final language = args[1] as String;
|
||||
final mnemonic = args[2] as String;
|
||||
|
||||
return WalletRestorationFromSeedVM(
|
||||
getIt.get<AppStore>(), getIt.get<WalletCreationService>(param1: type), _walletInfoSource,
|
||||
type: type, language: language, seed: mnemonic);
|
||||
});
|
||||
|
||||
getIt.registerFactoryParam<WalletRestorationFromKeysVM, List, void>((args, _) {
|
||||
final type = args.first as WalletType;
|
||||
final language = args[1] as String;
|
||||
|
||||
return WalletRestorationFromKeysVM(
|
||||
getIt.get<AppStore>(), getIt.get<WalletCreationService>(param1: type), _walletInfoSource,
|
||||
type: type, language: language);
|
||||
});
|
||||
|
||||
getIt.registerFactoryParam<WalletRestorationFromQRVM, WalletType, void>((WalletType type, _) {
|
||||
return WalletRestorationFromQRVM(getIt.get<AppStore>(),
|
||||
getIt.get<WalletCreationService>(param1: type), _walletInfoSource, type);
|
||||
|
@ -544,8 +523,7 @@ Future setup({
|
|||
|
||||
getIt.registerFactory<SendViewModel>(
|
||||
() => SendViewModel(
|
||||
getIt.get<AppStore>().wallet!,
|
||||
getIt.get<AppStore>().settingsStore,
|
||||
getIt.get<AppStore>(),
|
||||
getIt.get<SendTemplateViewModel>(),
|
||||
getIt.get<FiatConversionStore>(),
|
||||
getIt.get<BalanceViewModel>(),
|
||||
|
@ -723,7 +701,7 @@ Future setup({
|
|||
));
|
||||
|
||||
getIt.registerFactory(() => ExchangeViewModel(
|
||||
getIt.get<AppStore>().wallet!,
|
||||
getIt.get<AppStore>(),
|
||||
_tradesSource,
|
||||
getIt.get<ExchangeTemplateStore>(),
|
||||
getIt.get<TradesStore>(),
|
||||
|
@ -752,11 +730,11 @@ Future setup({
|
|||
case WalletType.haven:
|
||||
return haven!.createHavenWalletService(_walletInfoSource);
|
||||
case WalletType.monero:
|
||||
return monero!.createMoneroWalletService(_walletInfoSource);
|
||||
return monero!.createMoneroWalletService(_walletInfoSource, _unspentCoinsInfoSource);
|
||||
case WalletType.bitcoin:
|
||||
return bitcoin!.createBitcoinWalletService(_walletInfoSource, _unspentCoinsInfoSource!);
|
||||
return bitcoin!.createBitcoinWalletService(_walletInfoSource, _unspentCoinsInfoSource);
|
||||
case WalletType.litecoin:
|
||||
return bitcoin!.createLitecoinWalletService(_walletInfoSource, _unspentCoinsInfoSource!);
|
||||
return bitcoin!.createLitecoinWalletService(_walletInfoSource, _unspentCoinsInfoSource);
|
||||
case WalletType.ethereum:
|
||||
return ethereum!.createEthereumWalletService(_walletInfoSource);
|
||||
default:
|
||||
|
|
13
lib/entities/auto_generate_subaddress_status.dart
Normal file
13
lib/entities/auto_generate_subaddress_status.dart
Normal file
|
@ -0,0 +1,13 @@
|
|||
|
||||
enum AutoGenerateSubaddressStatus {
|
||||
initialized(1),
|
||||
enabled(2),
|
||||
disabled(3);
|
||||
|
||||
const AutoGenerateSubaddressStatus(this.value);
|
||||
final int value;
|
||||
|
||||
static AutoGenerateSubaddressStatus deserialize({required int raw}) =>
|
||||
AutoGenerateSubaddressStatus.values.firstWhere((e) => e.value == raw);
|
||||
|
||||
}
|
|
@ -1,12 +1,11 @@
|
|||
import 'dart:io' show File, Platform;
|
||||
import 'dart:io' show Directory, File, Platform;
|
||||
import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
||||
import 'package:cake_wallet/entities/exchange_api_mode.dart';
|
||||
import 'package:cw_core/pathForWallet.dart';
|
||||
import 'package:cake_wallet/entities/secret_store_key.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:share_plus/share_plus.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:cake_wallet/entities/preferences_key.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
|
@ -28,7 +27,7 @@ const cakeWalletLitecoinElectrumUri = 'ltc-electrum.cakewallet.com:50002';
|
|||
const havenDefaultNodeUri = 'nodes.havenprotocol.org:443';
|
||||
const ethereumDefaultNodeUri = 'ethereum.publicnode.com';
|
||||
|
||||
Future defaultSettingsMigration(
|
||||
Future<void> defaultSettingsMigration(
|
||||
{required int version,
|
||||
required SharedPreferences sharedPreferences,
|
||||
required FlutterSecureStorage secureStorage,
|
||||
|
@ -43,6 +42,8 @@ Future defaultSettingsMigration(
|
|||
// check current nodes for nullability regardless of the version
|
||||
await checkCurrentNodes(nodes, sharedPreferences);
|
||||
|
||||
await _validateWalletInfoBoxData(walletInfoSource);
|
||||
|
||||
final isNewInstall = sharedPreferences
|
||||
.getInt(PreferencesKey.currentDefaultSettingsMigrationVersion) == null;
|
||||
|
||||
|
@ -179,6 +180,66 @@ Future defaultSettingsMigration(
|
|||
PreferencesKey.currentDefaultSettingsMigrationVersion, version);
|
||||
}
|
||||
|
||||
Future<void> _validateWalletInfoBoxData(Box<WalletInfo> walletInfoSource) async {
|
||||
final root = await getApplicationDocumentsDirectory();
|
||||
|
||||
for (var type in WalletType.values) {
|
||||
if (type == WalletType.none) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String prefix = walletTypeToString(type).toLowerCase();
|
||||
Directory walletsDir = Directory('${root.path}/wallets/$prefix/');
|
||||
|
||||
if (!walletsDir.existsSync()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
List<String> walletNames = walletsDir.listSync().map((e) => e.path.split("/").last).toList();
|
||||
|
||||
for (var name in walletNames) {
|
||||
final dir = Directory(await pathForWalletDir(name: name, type: type));
|
||||
|
||||
final walletFiles = dir.listSync();
|
||||
final hasCacheFile = walletFiles.any((element) => element.path.contains("$name/$name"));
|
||||
|
||||
if (!hasCacheFile) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (type == WalletType.monero || type == WalletType.haven) {
|
||||
final hasKeysFile = walletFiles.any((element) => element.path.contains(".keys"));
|
||||
|
||||
if (!hasKeysFile) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
final id = prefix + '_' + name;
|
||||
final exist = walletInfoSource.values.any((el) => el.id == id);
|
||||
|
||||
if (exist) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final walletInfo = WalletInfo.external(
|
||||
id: id,
|
||||
type: type,
|
||||
name: name,
|
||||
isRecovery: true,
|
||||
restoreHeight: 0,
|
||||
date: DateTime.now(),
|
||||
dirPath: dir.path,
|
||||
path: '${dir.path}/$name',
|
||||
address: '',
|
||||
showIntroCakePayCard: false,
|
||||
);
|
||||
|
||||
walletInfoSource.add(walletInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> validateBitcoinSavedTransactionPriority(SharedPreferences sharedPreferences) async {
|
||||
if (bitcoin == null) {
|
||||
return;
|
||||
|
@ -226,7 +287,7 @@ Future<void> changeMoneroCurrentNodeToDefault(
|
|||
{required SharedPreferences sharedPreferences,
|
||||
required Box<Node> nodes}) async {
|
||||
final node = getMoneroDefaultNode(nodes: nodes);
|
||||
final nodeId = node?.key as int ?? 0; // 0 - England
|
||||
final nodeId = node.key as int? ?? 0; // 0 - England
|
||||
|
||||
await sharedPreferences.setInt(PreferencesKey.currentNodeIdKey, nodeId);
|
||||
}
|
||||
|
@ -279,7 +340,7 @@ Future<void> changeBitcoinCurrentElectrumServerToDefault(
|
|||
{required SharedPreferences sharedPreferences,
|
||||
required Box<Node> nodes}) async {
|
||||
final server = getBitcoinDefaultElectrumServer(nodes: nodes);
|
||||
final serverId = server?.key as int ?? 0;
|
||||
final serverId = server?.key as int? ?? 0;
|
||||
|
||||
await sharedPreferences.setInt(PreferencesKey.currentBitcoinElectrumSererIdKey, serverId);
|
||||
}
|
||||
|
@ -288,7 +349,7 @@ Future<void> changeLitecoinCurrentElectrumServerToDefault(
|
|||
{required SharedPreferences sharedPreferences,
|
||||
required Box<Node> nodes}) async {
|
||||
final server = getLitecoinDefaultElectrumServer(nodes: nodes);
|
||||
final serverId = server?.key as int ?? 0;
|
||||
final serverId = server?.key as int? ?? 0;
|
||||
|
||||
await sharedPreferences.setInt(PreferencesKey.currentLitecoinElectrumSererIdKey, serverId);
|
||||
}
|
||||
|
@ -297,7 +358,7 @@ Future<void> changeHavenCurrentNodeToDefault(
|
|||
{required SharedPreferences sharedPreferences,
|
||||
required Box<Node> nodes}) async {
|
||||
final node = getHavenDefaultNode(nodes: nodes);
|
||||
final nodeId = node?.key as int ?? 0;
|
||||
final nodeId = node?.key as int? ?? 0;
|
||||
|
||||
await sharedPreferences.setInt(PreferencesKey.currentHavenNodeIdKey, nodeId);
|
||||
}
|
||||
|
|
|
@ -49,7 +49,7 @@ class MainActions {
|
|||
case WalletType.ethereum:
|
||||
case WalletType.monero:
|
||||
if (viewModel.isEnabledBuyAction) {
|
||||
final uri = getIt.get<OnRamperBuyProvider>().requestUrl();
|
||||
final uri = getIt.get<OnRamperBuyProvider>().requestUrl(context);
|
||||
if (DeviceInfo.instance.isMobile) {
|
||||
Navigator.of(context)
|
||||
.pushNamed(Routes.webViewPage, arguments: [S.of(context).buy, uri]);
|
||||
|
|
|
@ -50,6 +50,7 @@ class PreferencesKey {
|
|||
'${PreferencesKey.moneroWalletPasswordUpdateV1Base}_${name}';
|
||||
|
||||
static const exchangeProvidersSelection = 'exchange-providers-selection';
|
||||
static const autoGenerateSubaddressStatusKey = 'auto_generate_subaddress_status';
|
||||
static const clearnetDonationLink = 'clearnet_donation_link';
|
||||
static const onionDonationLink = 'onion_donation_link';
|
||||
static const lastSeenAppVersion = 'last_seen_app_version';
|
||||
|
|
18
lib/entities/unspent_transaction_output.dart
Normal file
18
lib/entities/unspent_transaction_output.dart
Normal file
|
@ -0,0 +1,18 @@
|
|||
class Unspent {
|
||||
Unspent(this.address, this.hash, this.value, this.vout, this.keyImage)
|
||||
: isSending = true,
|
||||
isFrozen = false,
|
||||
note = '';
|
||||
|
||||
final String address;
|
||||
final String hash;
|
||||
final int value;
|
||||
final int vout;
|
||||
final String? keyImage;
|
||||
|
||||
bool isSending;
|
||||
bool isFrozen;
|
||||
String note;
|
||||
|
||||
bool get isP2wpkh => address.startsWith('bc') || address.startsWith('ltc');
|
||||
}
|
|
@ -22,6 +22,14 @@ class CWEthereum extends Ethereum {
|
|||
}) =>
|
||||
EthereumRestoreWalletFromSeedCredentials(name: name, password: password, mnemonic: mnemonic);
|
||||
|
||||
@override
|
||||
WalletCredentials createEthereumRestoreWalletFromPrivateKey({
|
||||
required String name,
|
||||
required String privateKey,
|
||||
required String password,
|
||||
}) =>
|
||||
EthereumRestoreWalletFromPrivateKey(name: name, password: password, privateKey: privateKey);
|
||||
|
||||
@override
|
||||
String getAddress(WalletBase wallet) => (wallet as EthereumWallet).walletAddresses.address;
|
||||
|
||||
|
|
|
@ -68,14 +68,12 @@ class ChangeNowExchangeProvider extends ExchangeProvider {
|
|||
required CryptoCurrency to,
|
||||
required bool isFixedRateMode}) async {
|
||||
final headers = {apiHeaderKey: apiKey};
|
||||
final normalizedFrom = normalizeCryptoCurrency(from);
|
||||
final normalizedTo = normalizeCryptoCurrency(to);
|
||||
final flow = getFlow(isFixedRateMode);
|
||||
final params = <String, String>{
|
||||
'fromCurrency': normalizedFrom,
|
||||
'toCurrency': normalizedTo,
|
||||
'fromNetwork': networkFor(from),
|
||||
'toNetwork': networkFor(to),
|
||||
'fromCurrency': _normalizeCurrency(from),
|
||||
'toCurrency': _normalizeCurrency(to),
|
||||
'fromNetwork': _networkFor(from),
|
||||
'toNetwork': _networkFor(to),
|
||||
'flow': flow
|
||||
};
|
||||
final uri = Uri.https(apiAuthority, rangePath, params);
|
||||
|
@ -112,10 +110,10 @@ class ChangeNowExchangeProvider extends ExchangeProvider {
|
|||
final flow = getFlow(isFixedRateMode);
|
||||
final type = isFixedRateMode ? 'reverse' : 'direct';
|
||||
final body = <String, dynamic>{
|
||||
'fromCurrency': normalizeCryptoCurrency(_request.from),
|
||||
'toCurrency': normalizeCryptoCurrency(_request.to),
|
||||
'fromNetwork': networkFor(_request.from),
|
||||
'toNetwork': networkFor(_request.to),
|
||||
'fromCurrency': _normalizeCurrency(_request.from),
|
||||
'toCurrency': _normalizeCurrency(_request.to),
|
||||
'fromNetwork': _networkFor(_request.from),
|
||||
'toNetwork': _networkFor(_request.to),
|
||||
if (!isFixedRateMode) 'fromAmount': _request.fromAmount,
|
||||
if (isFixedRateMode) 'toAmount': _request.toAmount,
|
||||
'address': _request.address,
|
||||
|
@ -241,10 +239,10 @@ class ChangeNowExchangeProvider extends ExchangeProvider {
|
|||
final type = isReverse ? 'reverse' : 'direct';
|
||||
final flow = getFlow(isFixedRateMode);
|
||||
final params = <String, String>{
|
||||
'fromCurrency': normalizeCryptoCurrency(from),
|
||||
'toCurrency': normalizeCryptoCurrency(to),
|
||||
'fromNetwork': networkFor(from),
|
||||
'toNetwork': networkFor(to),
|
||||
'fromCurrency': _normalizeCurrency(from),
|
||||
'toCurrency': _normalizeCurrency(to),
|
||||
'fromNetwork': _networkFor(from),
|
||||
'toNetwork': _networkFor(to),
|
||||
'type': type,
|
||||
'flow': flow
|
||||
};
|
||||
|
@ -273,25 +271,34 @@ class ChangeNowExchangeProvider extends ExchangeProvider {
|
|||
}
|
||||
}
|
||||
|
||||
String networkFor(CryptoCurrency currency) {
|
||||
String _networkFor(CryptoCurrency currency) {
|
||||
switch (currency) {
|
||||
case CryptoCurrency.usdt:
|
||||
return CryptoCurrency.btc.title.toLowerCase();
|
||||
return 'btc';
|
||||
default:
|
||||
return currency.tag != null ? currency.tag!.toLowerCase() : currency.title.toLowerCase();
|
||||
return currency.tag != null ? _normalizeTag(currency.tag!) : currency.title.toLowerCase();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String normalizeCryptoCurrency(CryptoCurrency currency) {
|
||||
switch (currency) {
|
||||
case CryptoCurrency.zec:
|
||||
return 'zec';
|
||||
case CryptoCurrency.usdcpoly:
|
||||
return 'usdcmatic';
|
||||
case CryptoCurrency.maticpoly:
|
||||
return 'maticmainnet';
|
||||
default:
|
||||
return currency.title.toLowerCase();
|
||||
String _normalizeCurrency(CryptoCurrency currency) {
|
||||
switch (currency) {
|
||||
case CryptoCurrency.zec:
|
||||
return 'zec';
|
||||
default:
|
||||
return currency.title.toLowerCase();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String _normalizeTag(String tag) {
|
||||
switch (tag) {
|
||||
case 'POLY':
|
||||
return 'matic';
|
||||
case 'LN':
|
||||
return 'lightning';
|
||||
case 'AVAXC':
|
||||
return 'cchain';
|
||||
default:
|
||||
return tag.toLowerCase();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -28,7 +28,6 @@ class SideShiftExchangeProvider extends ExchangeProvider {
|
|||
CryptoCurrency.xhv,
|
||||
CryptoCurrency.dcr,
|
||||
CryptoCurrency.kmd,
|
||||
CryptoCurrency.mkr,
|
||||
CryptoCurrency.oxt,
|
||||
CryptoCurrency.pivx,
|
||||
CryptoCurrency.rune,
|
||||
|
|
|
@ -20,7 +20,6 @@ class TrocadorExchangeProvider extends ExchangeProvider {
|
|||
bool useTorOnly;
|
||||
|
||||
static const List<CryptoCurrency> _notSupported = [
|
||||
CryptoCurrency.scrt,
|
||||
CryptoCurrency.stx,
|
||||
CryptoCurrency.zaddr,
|
||||
];
|
||||
|
@ -60,8 +59,8 @@ class TrocadorExchangeProvider extends ExchangeProvider {
|
|||
}) async {
|
||||
final params = <String, String>{
|
||||
'api_key': apiKey,
|
||||
'ticker_from': request.from.title.toLowerCase(),
|
||||
'ticker_to': request.to.title.toLowerCase(),
|
||||
'ticker_from': _normalizeCurrency(request.from),
|
||||
'ticker_to': _normalizeCurrency(request.to),
|
||||
'network_from': _networkFor(request.from),
|
||||
'network_to': _networkFor(request.to),
|
||||
'payment': isFixedRateMode ? 'True' : 'False',
|
||||
|
@ -137,7 +136,7 @@ class TrocadorExchangeProvider extends ExchangeProvider {
|
|||
required bool isFixedRateMode}) async {
|
||||
final params = <String, String>{
|
||||
'api_key': apiKey,
|
||||
'ticker': from.title.toLowerCase(),
|
||||
'ticker': _normalizeCurrency(from),
|
||||
'name': from.name,
|
||||
};
|
||||
|
||||
|
@ -177,8 +176,8 @@ class TrocadorExchangeProvider extends ExchangeProvider {
|
|||
|
||||
final params = <String, String>{
|
||||
'api_key': apiKey,
|
||||
'ticker_from': from.title.toLowerCase(),
|
||||
'ticker_to': to.title.toLowerCase(),
|
||||
'ticker_from': _normalizeCurrency(from),
|
||||
'ticker_to': _normalizeCurrency(to),
|
||||
'network_from': _networkFor(from),
|
||||
'network_to': _networkFor(to),
|
||||
if (!isFixedRateMode) 'amount_from': amount.toString(),
|
||||
|
@ -279,6 +278,15 @@ class TrocadorExchangeProvider extends ExchangeProvider {
|
|||
}
|
||||
}
|
||||
|
||||
String _normalizeCurrency(CryptoCurrency currency) {
|
||||
switch (currency) {
|
||||
case CryptoCurrency.zec:
|
||||
return 'zec';
|
||||
default:
|
||||
return currency.title.toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
String _normalizeTag(String tag) {
|
||||
switch (tag) {
|
||||
case 'ETH':
|
||||
|
|
|
@ -6,6 +6,7 @@ import 'package:cake_wallet/buy/order.dart';
|
|||
import 'package:cake_wallet/locales/locale.dart';
|
||||
import 'package:cake_wallet/store/yat/yat_store.dart';
|
||||
import 'package:cake_wallet/utils/exception_handler.dart';
|
||||
import 'package:cw_core/address_info.dart';
|
||||
import 'package:cake_wallet/utils/responsive_layout_util.dart';
|
||||
import 'package:cw_core/hive_type_ids.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
@ -89,6 +90,10 @@ Future<void> initializeAppConfigs() async {
|
|||
CakeHive.registerAdapter(TradeAdapter());
|
||||
}
|
||||
|
||||
if (!CakeHive.isAdapterRegistered(AddressInfo.typeId)) {
|
||||
CakeHive.registerAdapter(AddressInfoAdapter());
|
||||
}
|
||||
|
||||
if (!CakeHive.isAdapterRegistered(WalletInfo.typeId)) {
|
||||
CakeHive.registerAdapter(WalletInfoAdapter());
|
||||
}
|
||||
|
@ -133,11 +138,7 @@ Future<void> initializeAppConfigs() async {
|
|||
final templates = await CakeHive.openBox<Template>(Template.boxName);
|
||||
final exchangeTemplates = await CakeHive.openBox<ExchangeTemplate>(ExchangeTemplate.boxName);
|
||||
final anonpayInvoiceInfo = await CakeHive.openBox<AnonpayInvoiceInfo>(AnonpayInvoiceInfo.boxName);
|
||||
Box<UnspentCoinsInfo>? unspentCoinsInfoSource;
|
||||
|
||||
if (!isMoneroOnly) {
|
||||
unspentCoinsInfoSource = await CakeHive.openBox<UnspentCoinsInfo>(UnspentCoinsInfo.boxName);
|
||||
}
|
||||
final unspentCoinsInfoSource = await CakeHive.openBox<UnspentCoinsInfo>(UnspentCoinsInfo.boxName);
|
||||
|
||||
await initialSetup(
|
||||
sharedPreferences: await SharedPreferences.getInstance(),
|
||||
|
@ -169,7 +170,7 @@ Future<void> initialSetup(
|
|||
required Box<TransactionDescription> transactionDescriptions,
|
||||
required FlutterSecureStorage secureStorage,
|
||||
required Box<AnonpayInvoiceInfo> anonpayInvoiceInfo,
|
||||
Box<UnspentCoinsInfo>? unspentCoinsInfoSource,
|
||||
required Box<UnspentCoinsInfo> unspentCoinsInfoSource,
|
||||
int initialMigrationVersion = 15}) async {
|
||||
LanguageService.loadLocaleList();
|
||||
await defaultSettingsMigration(
|
||||
|
|
|
@ -1,361 +1,363 @@
|
|||
part of 'monero.dart';
|
||||
|
||||
class CWMoneroAccountList extends MoneroAccountList {
|
||||
CWMoneroAccountList(this._wallet);
|
||||
final Object _wallet;
|
||||
CWMoneroAccountList(this._wallet);
|
||||
|
||||
@override
|
||||
@computed
|
||||
final Object _wallet;
|
||||
|
||||
@override
|
||||
@computed
|
||||
ObservableList<Account> get accounts {
|
||||
final moneroWallet = _wallet as MoneroWallet;
|
||||
final accounts = moneroWallet.walletAddresses.accountList
|
||||
.accounts
|
||||
.map((acc) => Account(id: acc.id, label: acc.label, balance: acc.balance))
|
||||
.toList();
|
||||
return ObservableList<Account>.of(accounts);
|
||||
final moneroWallet = _wallet as MoneroWallet;
|
||||
final accounts = moneroWallet.walletAddresses.accountList.accounts
|
||||
.map((acc) => Account(id: acc.id, label: acc.label, balance: acc.balance))
|
||||
.toList();
|
||||
return ObservableList<Account>.of(accounts);
|
||||
}
|
||||
|
||||
@override
|
||||
void update(Object wallet) {
|
||||
final moneroWallet = wallet as MoneroWallet;
|
||||
moneroWallet.walletAddresses.accountList.update();
|
||||
final moneroWallet = wallet as MoneroWallet;
|
||||
moneroWallet.walletAddresses.accountList.update();
|
||||
}
|
||||
|
||||
@override
|
||||
void refresh(Object wallet) {
|
||||
final moneroWallet = wallet as MoneroWallet;
|
||||
moneroWallet.walletAddresses.accountList.refresh();
|
||||
}
|
||||
void refresh(Object wallet) {
|
||||
final moneroWallet = wallet as MoneroWallet;
|
||||
moneroWallet.walletAddresses.accountList.refresh();
|
||||
}
|
||||
|
||||
@override
|
||||
@override
|
||||
List<Account> getAll(Object wallet) {
|
||||
final moneroWallet = wallet as MoneroWallet;
|
||||
return moneroWallet.walletAddresses.accountList
|
||||
.getAll()
|
||||
.map((acc) => Account(id: acc.id, label: acc.label, balance: acc.balance))
|
||||
.toList();
|
||||
final moneroWallet = wallet as MoneroWallet;
|
||||
return moneroWallet.walletAddresses.accountList
|
||||
.getAll()
|
||||
.map((acc) => Account(id: acc.id, label: acc.label, balance: acc.balance))
|
||||
.toList();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> addAccount(Object wallet, {required String label}) async {
|
||||
final moneroWallet = wallet as MoneroWallet;
|
||||
await moneroWallet.walletAddresses.accountList.addAccount(label: label);
|
||||
final moneroWallet = wallet as MoneroWallet;
|
||||
await moneroWallet.walletAddresses.accountList.addAccount(label: label);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> setLabelAccount(Object wallet, {required int accountIndex, required String label}) async {
|
||||
final moneroWallet = wallet as MoneroWallet;
|
||||
await moneroWallet.walletAddresses.accountList
|
||||
.setLabelAccount(
|
||||
accountIndex: accountIndex,
|
||||
label: label);
|
||||
Future<void> setLabelAccount(Object wallet,
|
||||
{required int accountIndex, required String label}) async {
|
||||
final moneroWallet = wallet as MoneroWallet;
|
||||
await moneroWallet.walletAddresses.accountList
|
||||
.setLabelAccount(accountIndex: accountIndex, label: label);
|
||||
}
|
||||
}
|
||||
|
||||
class CWMoneroSubaddressList extends MoneroSubaddressList {
|
||||
CWMoneroSubaddressList(this._wallet);
|
||||
final Object _wallet;
|
||||
CWMoneroSubaddressList(this._wallet);
|
||||
|
||||
@override
|
||||
@computed
|
||||
final Object _wallet;
|
||||
|
||||
@override
|
||||
@computed
|
||||
ObservableList<Subaddress> get subaddresses {
|
||||
final moneroWallet = _wallet as MoneroWallet;
|
||||
final subAddresses = moneroWallet.walletAddresses.subaddressList
|
||||
.subaddresses
|
||||
.map((sub) => Subaddress(
|
||||
id: sub.id,
|
||||
address: sub.address,
|
||||
label: sub.label))
|
||||
.toList();
|
||||
return ObservableList<Subaddress>.of(subAddresses);
|
||||
final moneroWallet = _wallet as MoneroWallet;
|
||||
final subAddresses = moneroWallet.walletAddresses.subaddressList.subaddresses
|
||||
.map((sub) => Subaddress(id: sub.id, address: sub.address, label: sub.label))
|
||||
.toList();
|
||||
return ObservableList<Subaddress>.of(subAddresses);
|
||||
}
|
||||
|
||||
@override
|
||||
void update(Object wallet, {required int accountIndex}) {
|
||||
final moneroWallet = wallet as MoneroWallet;
|
||||
moneroWallet.walletAddresses.subaddressList.update(accountIndex: accountIndex);
|
||||
final moneroWallet = wallet as MoneroWallet;
|
||||
moneroWallet.walletAddresses.subaddressList.update(accountIndex: accountIndex);
|
||||
}
|
||||
|
||||
@override
|
||||
void refresh(Object wallet, {required int accountIndex}) {
|
||||
final moneroWallet = wallet as MoneroWallet;
|
||||
moneroWallet.walletAddresses.subaddressList.refresh(accountIndex: accountIndex);
|
||||
final moneroWallet = wallet as MoneroWallet;
|
||||
moneroWallet.walletAddresses.subaddressList.refresh(accountIndex: accountIndex);
|
||||
}
|
||||
|
||||
@override
|
||||
List<Subaddress> getAll(Object wallet) {
|
||||
final moneroWallet = wallet as MoneroWallet;
|
||||
return moneroWallet.walletAddresses
|
||||
.subaddressList
|
||||
.getAll()
|
||||
.map((sub) => Subaddress(id: sub.id, label: sub.label, address: sub.address))
|
||||
.toList();
|
||||
final moneroWallet = wallet as MoneroWallet;
|
||||
return moneroWallet.walletAddresses.subaddressList
|
||||
.getAll()
|
||||
.map((sub) => Subaddress(id: sub.id, label: sub.label, address: sub.address))
|
||||
.toList();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> addSubaddress(Object wallet, {required int accountIndex, required String label}) async {
|
||||
final moneroWallet = wallet as MoneroWallet;
|
||||
await moneroWallet.walletAddresses.subaddressList
|
||||
.addSubaddress(
|
||||
accountIndex: accountIndex,
|
||||
label: label);
|
||||
Future<void> addSubaddress(Object wallet,
|
||||
{required int accountIndex, required String label}) async {
|
||||
final moneroWallet = wallet as MoneroWallet;
|
||||
await moneroWallet.walletAddresses.subaddressList
|
||||
.addSubaddress(accountIndex: accountIndex, label: label);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> setLabelSubaddress(Object wallet,
|
||||
{required int accountIndex, required int addressIndex, required String label}) async {
|
||||
final moneroWallet = wallet as MoneroWallet;
|
||||
await moneroWallet.walletAddresses.subaddressList
|
||||
.setLabelSubaddress(
|
||||
accountIndex: accountIndex,
|
||||
addressIndex: addressIndex,
|
||||
label: label);
|
||||
final moneroWallet = wallet as MoneroWallet;
|
||||
await moneroWallet.walletAddresses.subaddressList
|
||||
.setLabelSubaddress(accountIndex: accountIndex, addressIndex: addressIndex, label: label);
|
||||
}
|
||||
}
|
||||
|
||||
class CWMoneroWalletDetails extends MoneroWalletDetails {
|
||||
CWMoneroWalletDetails(this._wallet);
|
||||
final Object _wallet;
|
||||
CWMoneroWalletDetails(this._wallet);
|
||||
|
||||
@computed
|
||||
final Object _wallet;
|
||||
|
||||
@computed
|
||||
@override
|
||||
Account get account {
|
||||
final moneroWallet = _wallet as MoneroWallet;
|
||||
final acc = moneroWallet.walletAddresses.account;
|
||||
return Account(id: acc!.id, label: acc.label, balance: acc.balance);
|
||||
final moneroWallet = _wallet as MoneroWallet;
|
||||
final acc = moneroWallet.walletAddresses.account;
|
||||
return Account(id: acc!.id, label: acc.label, balance: acc.balance);
|
||||
}
|
||||
|
||||
@computed
|
||||
@override
|
||||
MoneroBalance get balance {
|
||||
final moneroWallet = _wallet as MoneroWallet;
|
||||
final balance = moneroWallet.balance;
|
||||
MoneroBalance get balance {
|
||||
final moneroWallet = _wallet as MoneroWallet;
|
||||
final balance = moneroWallet.balance;
|
||||
throw Exception('Unimplemented');
|
||||
// return MoneroBalance();
|
||||
//return MoneroBalance(
|
||||
// fullBalance: balance.fullBalance,
|
||||
// unlockedBalance: balance.unlockedBalance);
|
||||
}
|
||||
// return MoneroBalance();
|
||||
//return MoneroBalance(
|
||||
// fullBalance: balance.fullBalance,
|
||||
// unlockedBalance: balance.unlockedBalance);
|
||||
}
|
||||
}
|
||||
|
||||
class CWMonero extends Monero {
|
||||
@override
|
||||
MoneroAccountList getAccountList(Object wallet) {
|
||||
return CWMoneroAccountList(wallet);
|
||||
}
|
||||
|
||||
@override
|
||||
MoneroSubaddressList getSubaddressList(Object wallet) {
|
||||
return CWMoneroSubaddressList(wallet);
|
||||
}
|
||||
|
||||
@override
|
||||
TransactionHistoryBase getTransactionHistory(Object wallet) {
|
||||
final moneroWallet = wallet as MoneroWallet;
|
||||
return moneroWallet.transactionHistory;
|
||||
}
|
||||
|
||||
@override
|
||||
MoneroWalletDetails getMoneroWalletDetails(Object wallet) {
|
||||
return CWMoneroWalletDetails(wallet);
|
||||
}
|
||||
|
||||
@override
|
||||
int getHeigthByDate({required DateTime date}) {
|
||||
return getMoneroHeigthByDate(date: date);
|
||||
}
|
||||
|
||||
@override
|
||||
TransactionPriority getDefaultTransactionPriority() {
|
||||
return MoneroTransactionPriority.automatic;
|
||||
}
|
||||
MoneroAccountList getAccountList(Object wallet) {
|
||||
return CWMoneroAccountList(wallet);
|
||||
}
|
||||
|
||||
@override
|
||||
TransactionPriority getMoneroTransactionPrioritySlow()
|
||||
=> MoneroTransactionPriority.slow;
|
||||
MoneroSubaddressList getSubaddressList(Object wallet) {
|
||||
return CWMoneroSubaddressList(wallet);
|
||||
}
|
||||
|
||||
@override
|
||||
TransactionPriority getMoneroTransactionPriorityAutomatic()
|
||||
=> MoneroTransactionPriority.automatic;
|
||||
TransactionHistoryBase getTransactionHistory(Object wallet) {
|
||||
final moneroWallet = wallet as MoneroWallet;
|
||||
return moneroWallet.transactionHistory;
|
||||
}
|
||||
|
||||
@override
|
||||
TransactionPriority deserializeMoneroTransactionPriority({required int raw}) {
|
||||
return MoneroTransactionPriority.deserialize(raw: raw);
|
||||
}
|
||||
@override
|
||||
MoneroWalletDetails getMoneroWalletDetails(Object wallet) {
|
||||
return CWMoneroWalletDetails(wallet);
|
||||
}
|
||||
|
||||
@override
|
||||
List<TransactionPriority> getTransactionPriorities() {
|
||||
return MoneroTransactionPriority.all;
|
||||
}
|
||||
@override
|
||||
int getHeigthByDate({required DateTime date}) {
|
||||
return getMoneroHeigthByDate(date: date);
|
||||
}
|
||||
|
||||
@override
|
||||
List<String> getMoneroWordList(String language) {
|
||||
switch (language.toLowerCase()) {
|
||||
case 'english':
|
||||
return EnglishMnemonics.words;
|
||||
case 'chinese (simplified)':
|
||||
return ChineseSimplifiedMnemonics.words;
|
||||
case 'dutch':
|
||||
return DutchMnemonics.words;
|
||||
case 'german':
|
||||
return GermanMnemonics.words;
|
||||
case 'japanese':
|
||||
return JapaneseMnemonics.words;
|
||||
case 'portuguese':
|
||||
return PortugueseMnemonics.words;
|
||||
case 'russian':
|
||||
return RussianMnemonics.words;
|
||||
case 'spanish':
|
||||
return SpanishMnemonics.words;
|
||||
case 'french':
|
||||
return FrenchMnemonics.words;
|
||||
case 'italian':
|
||||
return ItalianMnemonics.words;
|
||||
default:
|
||||
return EnglishMnemonics.words;
|
||||
}
|
||||
}
|
||||
@override
|
||||
TransactionPriority getDefaultTransactionPriority() {
|
||||
return MoneroTransactionPriority.automatic;
|
||||
}
|
||||
|
||||
@override
|
||||
WalletCredentials createMoneroRestoreWalletFromKeysCredentials({
|
||||
required String name,
|
||||
required String spendKey,
|
||||
required String viewKey,
|
||||
required String address,
|
||||
required String password,
|
||||
required String language,
|
||||
required int height}) {
|
||||
return MoneroRestoreWalletFromKeysCredentials(
|
||||
name: name,
|
||||
spendKey: spendKey,
|
||||
viewKey: viewKey,
|
||||
address: address,
|
||||
password: password,
|
||||
language: language,
|
||||
height: height);
|
||||
}
|
||||
|
||||
@override
|
||||
WalletCredentials createMoneroRestoreWalletFromSeedCredentials({
|
||||
required String name,
|
||||
required String password,
|
||||
required int height,
|
||||
required String mnemonic}) {
|
||||
return MoneroRestoreWalletFromSeedCredentials(
|
||||
name: name,
|
||||
password: password,
|
||||
height: height,
|
||||
mnemonic: mnemonic);
|
||||
}
|
||||
@override
|
||||
TransactionPriority getMoneroTransactionPrioritySlow() => MoneroTransactionPriority.slow;
|
||||
|
||||
@override
|
||||
WalletCredentials createMoneroNewWalletCredentials({
|
||||
@override
|
||||
TransactionPriority getMoneroTransactionPriorityAutomatic() =>
|
||||
MoneroTransactionPriority.automatic;
|
||||
|
||||
@override
|
||||
TransactionPriority deserializeMoneroTransactionPriority({required int raw}) {
|
||||
return MoneroTransactionPriority.deserialize(raw: raw);
|
||||
}
|
||||
|
||||
@override
|
||||
List<TransactionPriority> getTransactionPriorities() {
|
||||
return MoneroTransactionPriority.all;
|
||||
}
|
||||
|
||||
@override
|
||||
List<String> getMoneroWordList(String language) {
|
||||
switch (language.toLowerCase()) {
|
||||
case 'english':
|
||||
return EnglishMnemonics.words;
|
||||
case 'chinese (simplified)':
|
||||
return ChineseSimplifiedMnemonics.words;
|
||||
case 'dutch':
|
||||
return DutchMnemonics.words;
|
||||
case 'german':
|
||||
return GermanMnemonics.words;
|
||||
case 'japanese':
|
||||
return JapaneseMnemonics.words;
|
||||
case 'portuguese':
|
||||
return PortugueseMnemonics.words;
|
||||
case 'russian':
|
||||
return RussianMnemonics.words;
|
||||
case 'spanish':
|
||||
return SpanishMnemonics.words;
|
||||
case 'french':
|
||||
return FrenchMnemonics.words;
|
||||
case 'italian':
|
||||
return ItalianMnemonics.words;
|
||||
default:
|
||||
return EnglishMnemonics.words;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
WalletCredentials createMoneroRestoreWalletFromKeysCredentials(
|
||||
{required String name,
|
||||
required String spendKey,
|
||||
required String viewKey,
|
||||
required String address,
|
||||
required String password,
|
||||
required String language,
|
||||
required int height}) {
|
||||
return MoneroRestoreWalletFromKeysCredentials(
|
||||
name: name,
|
||||
spendKey: spendKey,
|
||||
viewKey: viewKey,
|
||||
address: address,
|
||||
password: password,
|
||||
language: language,
|
||||
height: height);
|
||||
}
|
||||
|
||||
@override
|
||||
WalletCredentials createMoneroRestoreWalletFromSeedCredentials(
|
||||
{required String name,
|
||||
required String password,
|
||||
required int height,
|
||||
required String mnemonic}) {
|
||||
return MoneroRestoreWalletFromSeedCredentials(
|
||||
name: name, password: password, height: height, mnemonic: mnemonic);
|
||||
}
|
||||
|
||||
@override
|
||||
WalletCredentials createMoneroNewWalletCredentials({
|
||||
required String name,
|
||||
required String language,
|
||||
String? password,}) {
|
||||
return MoneroNewWalletCredentials(
|
||||
name: name,
|
||||
password: password,
|
||||
language: language);
|
||||
}
|
||||
String? password,
|
||||
}) {
|
||||
return MoneroNewWalletCredentials(name: name, password: password, language: language);
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, String> getKeys(Object wallet) {
|
||||
final moneroWallet = wallet as MoneroWallet;
|
||||
final keys = moneroWallet.keys;
|
||||
return <String, String>{
|
||||
'privateSpendKey': keys.privateSpendKey,
|
||||
@override
|
||||
Map<String, String> getKeys(Object wallet) {
|
||||
final moneroWallet = wallet as MoneroWallet;
|
||||
final keys = moneroWallet.keys;
|
||||
return <String, String>{
|
||||
'privateSpendKey': keys.privateSpendKey,
|
||||
'privateViewKey': keys.privateViewKey,
|
||||
'publicSpendKey': keys.publicSpendKey,
|
||||
'publicViewKey': keys.publicViewKey};
|
||||
}
|
||||
'publicViewKey': keys.publicViewKey
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
Object createMoneroTransactionCreationCredentials({
|
||||
required List<Output> outputs,
|
||||
required TransactionPriority priority}) {
|
||||
return MoneroTransactionCreationCredentials(
|
||||
outputs: 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 MoneroTransactionPriority);
|
||||
}
|
||||
@override
|
||||
Object createMoneroTransactionCreationCredentials(
|
||||
{required List<Output> outputs, required TransactionPriority priority}) {
|
||||
return MoneroTransactionCreationCredentials(
|
||||
outputs: 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 MoneroTransactionPriority);
|
||||
}
|
||||
|
||||
@override
|
||||
Object createMoneroTransactionCreationCredentialsRaw({
|
||||
required List<OutputInfo> outputs,
|
||||
required TransactionPriority priority}) {
|
||||
return MoneroTransactionCreationCredentials(
|
||||
outputs: outputs,
|
||||
priority: priority as MoneroTransactionPriority);
|
||||
}
|
||||
@override
|
||||
Object createMoneroTransactionCreationCredentialsRaw(
|
||||
{required List<OutputInfo> outputs, required TransactionPriority priority}) {
|
||||
return MoneroTransactionCreationCredentials(
|
||||
outputs: outputs, priority: priority as MoneroTransactionPriority);
|
||||
}
|
||||
|
||||
@override
|
||||
String formatterMoneroAmountToString({required int amount}) {
|
||||
return moneroAmountToString(amount: amount);
|
||||
}
|
||||
@override
|
||||
String formatterMoneroAmountToString({required int amount}) {
|
||||
return moneroAmountToString(amount: amount);
|
||||
}
|
||||
|
||||
@override
|
||||
double formatterMoneroAmountToDouble({required int amount}) {
|
||||
return moneroAmountToDouble(amount: amount);
|
||||
}
|
||||
@override
|
||||
double formatterMoneroAmountToDouble({required int amount}) {
|
||||
return moneroAmountToDouble(amount: amount);
|
||||
}
|
||||
|
||||
@override
|
||||
int formatterMoneroParseAmount({required String amount}) {
|
||||
return moneroParseAmount(amount: amount);
|
||||
}
|
||||
@override
|
||||
int formatterMoneroParseAmount({required String amount}) {
|
||||
return moneroParseAmount(amount: amount);
|
||||
}
|
||||
|
||||
@override
|
||||
Account getCurrentAccount(Object wallet) {
|
||||
final moneroWallet = wallet as MoneroWallet;
|
||||
final acc = moneroWallet.walletAddresses.account;
|
||||
return Account(id: acc!.id, label: acc.label, balance: acc.balance);
|
||||
}
|
||||
@override
|
||||
Account getCurrentAccount(Object wallet) {
|
||||
final moneroWallet = wallet as MoneroWallet;
|
||||
final acc = moneroWallet.walletAddresses.account;
|
||||
return Account(id: acc!.id, label: acc.label, balance: acc.balance);
|
||||
}
|
||||
|
||||
@override
|
||||
void setCurrentAccount(Object wallet, int id, String label, String? balance) {
|
||||
final moneroWallet = wallet as MoneroWallet;
|
||||
moneroWallet.walletAddresses.account = monero_account.Account(id: id, label: label, balance: balance);
|
||||
}
|
||||
@override
|
||||
void setCurrentAccount(Object wallet, int id, String label, String? balance) {
|
||||
final moneroWallet = wallet as MoneroWallet;
|
||||
moneroWallet.walletAddresses.account =
|
||||
monero_account.Account(id: id, label: label, balance: balance);
|
||||
}
|
||||
|
||||
@override
|
||||
void onStartup() {
|
||||
monero_wallet_api.onStartup();
|
||||
}
|
||||
@override
|
||||
void onStartup() {
|
||||
monero_wallet_api.onStartup();
|
||||
}
|
||||
|
||||
@override
|
||||
int getTransactionInfoAccountId(TransactionInfo tx) {
|
||||
final moneroTransactionInfo = tx as MoneroTransactionInfo;
|
||||
return moneroTransactionInfo.accountIndex;
|
||||
}
|
||||
@override
|
||||
int getTransactionInfoAccountId(TransactionInfo tx) {
|
||||
final moneroTransactionInfo = tx as MoneroTransactionInfo;
|
||||
return moneroTransactionInfo.accountIndex;
|
||||
}
|
||||
|
||||
@override
|
||||
WalletService createMoneroWalletService(Box<WalletInfo> walletInfoSource) {
|
||||
return MoneroWalletService(walletInfoSource);
|
||||
}
|
||||
@override
|
||||
WalletService createMoneroWalletService(
|
||||
Box<WalletInfo> walletInfoSource, Box<UnspentCoinsInfo> unspentCoinSource) {
|
||||
return MoneroWalletService(walletInfoSource, unspentCoinSource);
|
||||
}
|
||||
|
||||
@override
|
||||
String getTransactionAddress(Object wallet, int accountIndex, int addressIndex) {
|
||||
final moneroWallet = wallet as MoneroWallet;
|
||||
return moneroWallet.getTransactionAddress(accountIndex, addressIndex);
|
||||
}
|
||||
@override
|
||||
String getTransactionAddress(Object wallet, int accountIndex, int addressIndex) {
|
||||
final moneroWallet = wallet as MoneroWallet;
|
||||
return moneroWallet.getTransactionAddress(accountIndex, addressIndex);
|
||||
}
|
||||
|
||||
@override
|
||||
String getSubaddressLabel(Object wallet, int accountIndex, int addressIndex) {
|
||||
final moneroWallet = wallet as MoneroWallet;
|
||||
return moneroWallet.getSubaddressLabel(accountIndex, addressIndex);
|
||||
}
|
||||
@override
|
||||
String getSubaddressLabel(Object wallet, int accountIndex, int addressIndex) {
|
||||
final moneroWallet = wallet as MoneroWallet;
|
||||
return moneroWallet.getSubaddressLabel(accountIndex, addressIndex);
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, String> pendingTransactionInfo(Object transaction) {
|
||||
final ptx = transaction as PendingMoneroTransaction;
|
||||
return {'id': ptx.id, 'hex': ptx.hex, 'key': ptx.txKey};
|
||||
}
|
||||
@override
|
||||
Map<String, String> pendingTransactionInfo(Object transaction) {
|
||||
final ptx = transaction as PendingMoneroTransaction;
|
||||
return {'id': ptx.id, 'hex': ptx.hex, 'key': ptx.txKey};
|
||||
}
|
||||
|
||||
@override
|
||||
List<Unspent> getUnspents(Object wallet) {
|
||||
final moneroWallet = wallet as MoneroWallet;
|
||||
return moneroWallet.unspentCoins
|
||||
.map((MoneroUnspent moneroUnspent) => Unspent(moneroUnspent.address, moneroUnspent.hash,
|
||||
moneroUnspent.value, 0, moneroUnspent.keyImage))
|
||||
.toList();
|
||||
}
|
||||
|
||||
@override
|
||||
void updateUnspents(Object wallet) async {
|
||||
final moneroWallet = wallet as MoneroWallet;
|
||||
await moneroWallet.updateUnspent();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import 'package:cake_wallet/entities/auto_generate_subaddress_status.dart';
|
||||
import 'package:cake_wallet/entities/fiat_api_mode.dart';
|
||||
import 'package:cake_wallet/entities/update_haven_rate.dart';
|
||||
import 'package:cake_wallet/ethereum/ethereum.dart';
|
||||
|
@ -21,36 +22,36 @@ ReactionDisposer? _onCurrentWalletChangeReaction;
|
|||
ReactionDisposer? _onCurrentWalletChangeFiatRateUpdateReaction;
|
||||
//ReactionDisposer _onCurrentWalletAddressChangeReaction;
|
||||
|
||||
void startCurrentWalletChangeReaction(AppStore appStore,
|
||||
SettingsStore settingsStore, FiatConversionStore fiatConversionStore) {
|
||||
void startCurrentWalletChangeReaction(
|
||||
AppStore appStore, SettingsStore settingsStore, FiatConversionStore fiatConversionStore) {
|
||||
_onCurrentWalletChangeReaction?.reaction.dispose();
|
||||
_onCurrentWalletChangeFiatRateUpdateReaction?.reaction.dispose();
|
||||
//_onCurrentWalletAddressChangeReaction?.reaction?dispose();
|
||||
|
||||
//_onCurrentWalletAddressChangeReaction = reaction((_) => appStore.wallet.walletAddresses.address,
|
||||
//(String address) async {
|
||||
//if (address == appStore.wallet.walletInfo.yatLastUsedAddress) {
|
||||
// return;
|
||||
//}
|
||||
//(String address) async {
|
||||
//if (address == appStore.wallet.walletInfo.yatLastUsedAddress) {
|
||||
// return;
|
||||
//}
|
||||
|
||||
//try {
|
||||
// final yatStore = getIt.get<YatStore>();
|
||||
// await updateEmojiIdAddress(
|
||||
// appStore.wallet.walletInfo.yatEmojiId,
|
||||
// appStore.wallet.walletAddresses.address,
|
||||
// yatStore.apiKey,
|
||||
// appStore.wallet.type
|
||||
// );
|
||||
// appStore.wallet.walletInfo.yatLastUsedAddress = address;
|
||||
// await appStore.wallet.walletInfo.save();
|
||||
//} catch (e) {
|
||||
// print(e.toString());
|
||||
//}
|
||||
//try {
|
||||
// final yatStore = getIt.get<YatStore>();
|
||||
// await updateEmojiIdAddress(
|
||||
// appStore.wallet.walletInfo.yatEmojiId,
|
||||
// appStore.wallet.walletAddresses.address,
|
||||
// yatStore.apiKey,
|
||||
// appStore.wallet.type
|
||||
// );
|
||||
// appStore.wallet.walletInfo.yatLastUsedAddress = address;
|
||||
// await appStore.wallet.walletInfo.save();
|
||||
//} catch (e) {
|
||||
// print(e.toString());
|
||||
//}
|
||||
//});
|
||||
|
||||
_onCurrentWalletChangeReaction = reaction((_) => appStore.wallet, (WalletBase<
|
||||
Balance, TransactionHistoryBase<TransactionInfo>, TransactionInfo>?
|
||||
wallet) async {
|
||||
_onCurrentWalletChangeReaction = reaction((_) => appStore.wallet,
|
||||
(WalletBase<Balance, TransactionHistoryBase<TransactionInfo>, TransactionInfo>?
|
||||
wallet) async {
|
||||
try {
|
||||
if (wallet == null) {
|
||||
return;
|
||||
|
@ -59,11 +60,13 @@ void startCurrentWalletChangeReaction(AppStore appStore,
|
|||
final node = settingsStore.getCurrentNode(wallet.type);
|
||||
startWalletSyncStatusChangeReaction(wallet, fiatConversionStore);
|
||||
startCheckConnectionReaction(wallet, settingsStore);
|
||||
await getIt.get<SharedPreferences>().setString(PreferencesKey.currentWalletName, wallet.name);
|
||||
await getIt
|
||||
.get<SharedPreferences>()
|
||||
.setString(PreferencesKey.currentWalletName, wallet.name);
|
||||
await getIt.get<SharedPreferences>().setInt(
|
||||
PreferencesKey.currentWalletType, serializeToInt(wallet.type));
|
||||
.setInt(PreferencesKey.currentWalletType, serializeToInt(wallet.type));
|
||||
if (wallet.type == WalletType.monero) {
|
||||
_setAutoGenerateSubaddressStatus(wallet, settingsStore);
|
||||
}
|
||||
await wallet.connectToNode(node: node);
|
||||
|
||||
if (wallet.type == WalletType.haven) {
|
||||
|
@ -82,9 +85,8 @@ void startCurrentWalletChangeReaction(AppStore appStore,
|
|||
}
|
||||
});
|
||||
|
||||
_onCurrentWalletChangeFiatRateUpdateReaction =
|
||||
reaction((_) => appStore.wallet, (WalletBase<Balance,
|
||||
TransactionHistoryBase<TransactionInfo>, TransactionInfo>?
|
||||
_onCurrentWalletChangeFiatRateUpdateReaction = reaction((_) => appStore.wallet,
|
||||
(WalletBase<Balance, TransactionHistoryBase<TransactionInfo>, TransactionInfo>?
|
||||
wallet) async {
|
||||
try {
|
||||
if (wallet == null || settingsStore.fiatApiMode == FiatApiMode.disabled) {
|
||||
|
@ -92,11 +94,10 @@ void startCurrentWalletChangeReaction(AppStore appStore,
|
|||
}
|
||||
|
||||
fiatConversionStore.prices[wallet.currency] = 0;
|
||||
fiatConversionStore.prices[wallet.currency] =
|
||||
await FiatConversionService.fetchPrice(
|
||||
crypto: wallet.currency,
|
||||
fiat: settingsStore.fiatCurrency,
|
||||
torOnly: settingsStore.fiatApiMode == FiatApiMode.torOnly);
|
||||
fiatConversionStore.prices[wallet.currency] = await FiatConversionService.fetchPrice(
|
||||
crypto: wallet.currency,
|
||||
fiat: settingsStore.fiatCurrency,
|
||||
torOnly: settingsStore.fiatApiMode == FiatApiMode.torOnly);
|
||||
|
||||
if (wallet.type == WalletType.ethereum) {
|
||||
final currencies =
|
||||
|
@ -116,3 +117,17 @@ void startCurrentWalletChangeReaction(AppStore appStore,
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
void _setAutoGenerateSubaddressStatus(
|
||||
WalletBase<Balance, TransactionHistoryBase<TransactionInfo>, TransactionInfo> wallet,
|
||||
SettingsStore settingsStore,
|
||||
) async {
|
||||
final walletHasAddresses = await wallet.walletAddresses.addressesMap.length > 1;
|
||||
if (settingsStore.autoGenerateSubaddressStatus == AutoGenerateSubaddressStatus.initialized &&
|
||||
walletHasAddresses) {
|
||||
settingsStore.autoGenerateSubaddressStatus = AutoGenerateSubaddressStatus.disabled;
|
||||
}
|
||||
wallet.isEnabledAutoGenerateSubaddress =
|
||||
settingsStore.autoGenerateSubaddressStatus == AutoGenerateSubaddressStatus.enabled ||
|
||||
settingsStore.autoGenerateSubaddressStatus == AutoGenerateSubaddressStatus.initialized;
|
||||
}
|
||||
|
|
|
@ -57,7 +57,6 @@ import 'package:cake_wallet/routes.dart';
|
|||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/di.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_new_vm.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_restoration_from_seed_vm.dart';
|
||||
import 'package:cake_wallet/exchange/trade.dart';
|
||||
import 'package:cw_core/transaction_info.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
|
@ -80,7 +79,6 @@ import 'package:cake_wallet/src/screens/monero_accounts/monero_account_edit_or_c
|
|||
import 'package:cake_wallet/src/screens/contact/contact_list_page.dart';
|
||||
import 'package:cake_wallet/src/screens/contact/contact_page.dart';
|
||||
import 'package:cake_wallet/src/screens/wallet_keys/wallet_keys_page.dart';
|
||||
import 'package:cake_wallet/src/screens/restore/restore_wallet_from_seed_details.dart';
|
||||
import 'package:cake_wallet/src/screens/exchange/exchange_page.dart';
|
||||
import 'package:cake_wallet/src/screens/rescan/rescan_page.dart';
|
||||
import 'package:cake_wallet/src/screens/faq/faq_page.dart';
|
||||
|
@ -398,16 +396,6 @@ Route<dynamic> createRoute(RouteSettings settings) {
|
|||
builder: (_) =>
|
||||
getIt.get<BuyWebViewPage>(param1: args));
|
||||
|
||||
case Routes.restoreWalletFromSeedDetails:
|
||||
final args = settings.arguments as List;
|
||||
final walletRestorationFromSeedVM =
|
||||
getIt.get<WalletRestorationFromSeedVM>(param1: args);
|
||||
|
||||
return CupertinoPageRoute<void>(
|
||||
fullscreenDialog: true,
|
||||
builder: (_) => RestoreWalletFromSeedDetailsPage(
|
||||
walletRestorationFromSeedVM: walletRestorationFromSeedVM));
|
||||
|
||||
case Routes.exchange:
|
||||
return CupertinoPageRoute<void>(
|
||||
fullscreenDialog: true,
|
||||
|
|
|
@ -30,7 +30,6 @@ class Routes {
|
|||
static const tradeDetails = '/trade_details';
|
||||
static const exchangeFunds = '/exchange_funds';
|
||||
static const exchangeTrade = '/exchange_trade';
|
||||
static const restoreWalletFromSeedDetails = '/restore_from_seed_details';
|
||||
static const exchange = '/exchange';
|
||||
static const settings = '/settings';
|
||||
static const desktop_settings_page = '/desktop_settings_page';
|
||||
|
|
|
@ -127,9 +127,9 @@ class BackupPage extends BasePage {
|
|||
builder: (dialogContext) {
|
||||
return AlertWithTwoActions(
|
||||
alertTitle: S.of(context).export_backup,
|
||||
alertContent: 'Please select destination for the backup file.',
|
||||
rightButtonText: 'Save to Downloads',
|
||||
leftButtonText: 'Share',
|
||||
alertContent: S.of(context).select_destination,
|
||||
rightButtonText: S.of(context).save_to_downloads,
|
||||
leftButtonText:S.of(context).share,
|
||||
actionRightButton: () async {
|
||||
final permission = await Permission.storage.request();
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import 'package:cake_wallet/core/auth_service.dart';
|
||||
import 'package:cake_wallet/entities/contact_base.dart';
|
||||
import 'package:cake_wallet/entities/contact_record.dart';
|
||||
import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
|
||||
import 'package:cake_wallet/themes/extensions/exchange_page_theme.dart';
|
||||
import 'package:cake_wallet/utils/show_bar.dart';
|
||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||
|
@ -39,7 +40,7 @@ class ContactListPage extends BasePage {
|
|||
children: <Widget>[
|
||||
Icon(
|
||||
Icons.add,
|
||||
color: Theme.of(context).dialogTheme.backgroundColor,
|
||||
color: Theme.of(context).appBarTheme.titleTextStyle!.color,
|
||||
size: 22.0,
|
||||
),
|
||||
ButtonTheme(
|
||||
|
@ -71,7 +72,7 @@ class ContactListPage extends BasePage {
|
|||
@override
|
||||
Widget body(BuildContext context) {
|
||||
return Container(
|
||||
padding: EdgeInsets.only(top: 20.0, bottom: 20.0),
|
||||
padding: EdgeInsets.all(20.0),
|
||||
child: Observer(builder: (_) {
|
||||
final contacts = contactListViewModel.contactsToShow;
|
||||
final walletContacts = contactListViewModel.walletContactsToShow;
|
||||
|
@ -131,7 +132,6 @@ class ContactListPage extends BasePage {
|
|||
}
|
||||
},
|
||||
child: Container(
|
||||
color: Colors.transparent,
|
||||
padding: const EdgeInsets.only(top: 16, bottom: 16, right: 24),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
|
@ -146,7 +146,7 @@ class ContactListPage extends BasePage {
|
|||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.normal,
|
||||
color: Theme.of(context).dialogTheme.backgroundColor,
|
||||
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
|
||||
),
|
||||
),
|
||||
))
|
||||
|
|
|
@ -73,12 +73,12 @@ class DesktopDashboardActions extends StatelessWidget {
|
|||
),
|
||||
],
|
||||
),
|
||||
Expanded(
|
||||
child: MarketPlacePage(
|
||||
dashboardViewModel: dashboardViewModel,
|
||||
marketPlaceViewModel: getIt.get<MarketPlaceViewModel>(),
|
||||
),
|
||||
Expanded(
|
||||
child: MarketPlacePage(
|
||||
dashboardViewModel: dashboardViewModel,
|
||||
marketPlaceViewModel: getIt.get<MarketPlaceViewModel>(),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
import 'package:cake_wallet/themes/extensions/keyboard_theme.dart';
|
||||
import 'package:cake_wallet/di.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/src/screens/monero_accounts/monero_account_list_page.dart';
|
||||
import 'package:cake_wallet/anonpay/anonpay_donation_link_info.dart';
|
||||
import 'package:cake_wallet/entities/preferences_key.dart';
|
||||
import 'package:cake_wallet/entities/receive_page_option.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/src/screens/dashboard/widgets/present_receive_option_picker.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
|
||||
import 'package:cake_wallet/src/widgets/gradient_background.dart';
|
||||
|
@ -24,7 +26,6 @@ import 'package:flutter_mobx/flutter_mobx.dart';
|
|||
import 'package:keyboard_actions/keyboard_actions.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:cake_wallet/di.dart';
|
||||
import 'package:cake_wallet/themes/extensions/dashboard_page_theme.dart';
|
||||
import 'package:cake_wallet/themes/extensions/balance_page_theme.dart';
|
||||
|
||||
|
@ -174,7 +175,11 @@ class AddressPage extends BasePage {
|
|||
Observer(builder: (_) {
|
||||
if (addressListViewModel.hasAddressList) {
|
||||
return GestureDetector(
|
||||
onTap: () => Navigator.of(context).pushNamed(Routes.receive),
|
||||
onTap: () async => dashboardViewModel.isAutoGenerateSubaddressesEnabled
|
||||
? await showPopUp<void>(
|
||||
context: context,
|
||||
builder: (_) => getIt.get<MoneroAccountListPage>())
|
||||
: Navigator.of(context).pushNamed(Routes.receive),
|
||||
child: Container(
|
||||
height: 50,
|
||||
padding: EdgeInsets.only(left: 24, right: 12),
|
||||
|
@ -182,9 +187,8 @@ class AddressPage extends BasePage {
|
|||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.all(Radius.circular(25)),
|
||||
border: Border.all(
|
||||
color: Theme.of(context)
|
||||
.extension<ReceivePageTheme>()!
|
||||
.iconsBackgroundColor,
|
||||
color:
|
||||
Theme.of(context).extension<BalancePageTheme>()!.cardBorderColor,
|
||||
width: 1),
|
||||
color: Theme.of(context)
|
||||
.extension<SyncIndicatorTheme>()!
|
||||
|
@ -194,26 +198,36 @@ class AddressPage extends BasePage {
|
|||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: <Widget>[
|
||||
Observer(
|
||||
builder: (_) => Text(
|
||||
addressListViewModel.hasAccounts
|
||||
? S.of(context).accounts_subaddresses
|
||||
: S.of(context).addresses,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Theme.of(context).extension<DashboardPageTheme>()!.textColor),
|
||||
)),
|
||||
builder: (_) {
|
||||
String label = addressListViewModel.hasAccounts
|
||||
? S.of(context).accounts_subaddresses
|
||||
: S.of(context).addresses;
|
||||
|
||||
if (dashboardViewModel.isAutoGenerateSubaddressesEnabled) {
|
||||
label = addressListViewModel.hasAccounts
|
||||
? S.of(context).accounts
|
||||
: S.of(context).account;
|
||||
}
|
||||
return Text(
|
||||
label,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Theme.of(context)
|
||||
.extension<SyncIndicatorTheme>()!
|
||||
.textColor),
|
||||
);
|
||||
},),
|
||||
Icon(
|
||||
Icons.arrow_forward_ios,
|
||||
size: 14,
|
||||
color:
|
||||
Theme.of(context).extension<DashboardPageTheme>()!.textColor,
|
||||
color: Theme.of(context).extension<SyncIndicatorTheme>()!.textColor,
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
} else if (addressListViewModel.showElectrumAddressDisclaimer) {
|
||||
} else if (dashboardViewModel.isAutoGenerateSubaddressesEnabled || addressListViewModel.showElectrumAddressDisclaimer) {
|
||||
return Text(S.of(context).electrum_address_disclaimer,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
|
|
|
@ -2,7 +2,6 @@ import 'package:cake_wallet/routes.dart';
|
|||
import 'package:cake_wallet/src/screens/exchange_trade/information_page.dart';
|
||||
import 'package:cake_wallet/store/settings_store.dart';
|
||||
import 'package:cake_wallet/themes/extensions/sync_indicator_theme.dart';
|
||||
import 'package:cake_wallet/themes/theme_base.dart';
|
||||
import 'package:cake_wallet/utils/feature_flag.dart';
|
||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
@ -77,9 +76,7 @@ class BalancePage extends StatelessWidget {
|
|||
return IntroducingCard(
|
||||
title: S.of(context).introducing_cake_pay,
|
||||
subTitle: S.of(context).cake_pay_learn_more,
|
||||
borderColor: settingsStore.currentTheme.type == ThemeType.bright
|
||||
? Color.fromRGBO(255, 255, 255, 0.2)
|
||||
: Colors.transparent,
|
||||
borderColor: Theme.of(context).extension<BalancePageTheme>()!.cardBorderColor,
|
||||
closeCard: dashboardViewModel.balanceViewModel.disableIntroCakePayCard);
|
||||
}
|
||||
return Container();
|
||||
|
@ -139,9 +136,7 @@ class BalancePage extends StatelessWidget {
|
|||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(30.0),
|
||||
border: Border.all(
|
||||
color: settingsStore.currentTheme.type == ThemeType.bright
|
||||
? Color.fromRGBO(255, 255, 255, 0.2)
|
||||
: Colors.transparent,
|
||||
color: Theme.of(context).extension<BalancePageTheme>()!.cardBorderColor,
|
||||
width: 1,
|
||||
),
|
||||
color: Theme.of(context).extension<SyncIndicatorTheme>()!.syncedBackgroundColor,
|
||||
|
@ -282,7 +277,7 @@ class BalancePage extends StatelessWidget {
|
|||
fontSize: 20,
|
||||
fontFamily: 'Lato',
|
||||
fontWeight: FontWeight.w400,
|
||||
color: Theme.of(context).extension<DashboardPageTheme>()!.textColor,
|
||||
color: Theme.of(context).extension<BalancePageTheme>()!.assetTitleColor,
|
||||
height: 1,
|
||||
),
|
||||
maxLines: 1,
|
||||
|
@ -296,7 +291,7 @@ class BalancePage extends StatelessWidget {
|
|||
fontSize: 12,
|
||||
fontFamily: 'Lato',
|
||||
fontWeight: FontWeight.w400,
|
||||
color: Theme.of(context).extension<DashboardPageTheme>()!.textColor,
|
||||
color: Theme.of(context).extension<BalancePageTheme>()!.textColor,
|
||||
height: 1,
|
||||
),
|
||||
),
|
||||
|
|
|
@ -48,15 +48,15 @@ class MarketPlacePage extends StatelessWidget {
|
|||
child: ListView(
|
||||
controller: _scrollController,
|
||||
children: <Widget>[
|
||||
SizedBox(height: 20),
|
||||
DashBoardRoundedCardWidget(
|
||||
onTap: () => launchUrl(
|
||||
Uri.parse("https://cakelabs.com/news/cake-pay-mobile-to-shut-down/"),
|
||||
mode: LaunchMode.externalApplication,
|
||||
),
|
||||
title: S.of(context).cake_pay_title,
|
||||
subTitle: S.of(context).cake_pay_subtitle,
|
||||
),
|
||||
// SizedBox(height: 20),
|
||||
// DashBoardRoundedCardWidget(
|
||||
// onTap: () => launchUrl(
|
||||
// Uri.parse("https://cakelabs.com/news/cake-pay-mobile-to-shut-down/"),
|
||||
// mode: LaunchMode.externalApplication,
|
||||
// ),
|
||||
// title: S.of(context).cake_pay_title,
|
||||
// subTitle: S.of(context).cake_pay_subtitle,
|
||||
// ),
|
||||
SizedBox(height: 20),
|
||||
DashBoardRoundedCardWidget(
|
||||
onTap: () => launchUrl(
|
||||
|
|
|
@ -5,6 +5,7 @@ import 'package:cake_wallet/di.dart';
|
|||
import 'package:cake_wallet/src/screens/exchange/widgets/desktop_exchange_cards_section.dart';
|
||||
import 'package:cake_wallet/src/screens/exchange/widgets/mobile_exchange_cards_section.dart';
|
||||
import 'package:cake_wallet/src/widgets/add_template_button.dart';
|
||||
import 'package:cake_wallet/themes/extensions/send_page_theme.dart';
|
||||
import 'package:cake_wallet/themes/theme_base.dart';
|
||||
import 'package:cake_wallet/utils/debounce.dart';
|
||||
import 'package:cake_wallet/utils/responsive_layout_util.dart';
|
||||
|
@ -629,7 +630,7 @@ class ExchangePage extends BasePage {
|
|||
},
|
||||
imageArrow: arrowBottomPurple,
|
||||
currencyButtonColor: Colors.transparent,
|
||||
addressButtonsColor: Theme.of(context).focusColor,
|
||||
addressButtonsColor: Theme.of(context).extension<SendPageTheme>()!.textFieldButtonColor,
|
||||
borderColor: Theme.of(context).extension<ExchangePageTheme>()!.textFieldBorderTopPanelColor,
|
||||
currencyValueValidator: (value) {
|
||||
return !exchangeViewModel.isFixedRateMode
|
||||
|
@ -677,7 +678,7 @@ class ExchangePage extends BasePage {
|
|||
exchangeViewModel.changeReceiveCurrency(currency: currency),
|
||||
imageArrow: arrowBottomCakeGreen,
|
||||
currencyButtonColor: Colors.transparent,
|
||||
addressButtonsColor: Theme.of(context).focusColor,
|
||||
addressButtonsColor: Theme.of(context).extension<SendPageTheme>()!.textFieldButtonColor,
|
||||
borderColor: Theme.of(context).extension<ExchangePageTheme>()!.textFieldBorderBottomPanelColor,
|
||||
currencyValueValidator: (value) {
|
||||
return exchangeViewModel.isFixedRateMode
|
||||
|
|
|
@ -5,13 +5,14 @@ import 'package:flutter_slidable/flutter_slidable.dart';
|
|||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
|
||||
class AccountTile extends StatelessWidget {
|
||||
AccountTile(
|
||||
{required this.isCurrent,
|
||||
required this.accountName,
|
||||
this.accountBalance,
|
||||
required this.currency,
|
||||
required this.onTap,
|
||||
required this.onEdit});
|
||||
AccountTile({
|
||||
required this.isCurrent,
|
||||
required this.accountName,
|
||||
this.accountBalance,
|
||||
required this.currency,
|
||||
required this.onTap,
|
||||
required this.onEdit,
|
||||
});
|
||||
|
||||
final bool isCurrent;
|
||||
final String accountName;
|
||||
|
|
|
@ -73,7 +73,7 @@ class RestoreOptionsPage extends BasePage {
|
|||
await restoreFromQRViewModel.create(restoreWallet: restoreWallet);
|
||||
if (restoreFromQRViewModel.state is FailureState) {
|
||||
_onWalletCreateFailure(context,
|
||||
'Create wallet state: ${restoreFromQRViewModel.state.runtimeType.toString()}');
|
||||
'Create wallet state: ${(restoreFromQRViewModel.state as FailureState).error}');
|
||||
}
|
||||
} catch (e) {
|
||||
_onWalletCreateFailure(context, e.toString());
|
||||
|
|
|
@ -1,145 +0,0 @@
|
|||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/core/wallet_name_validator.dart';
|
||||
import 'package:cake_wallet/core/execution_state.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/src/widgets/blockchain_height_widget.dart';
|
||||
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
|
||||
import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_restoration_from_seed_vm.dart';
|
||||
|
||||
class RestoreWalletFromSeedDetailsPage extends BasePage {
|
||||
RestoreWalletFromSeedDetailsPage(
|
||||
{required this.walletRestorationFromSeedVM});
|
||||
|
||||
final WalletRestorationFromSeedVM walletRestorationFromSeedVM;
|
||||
|
||||
@override
|
||||
String get title => S.current.restore_wallet_restore_description;
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) => RestoreFromSeedDetailsForm(
|
||||
walletRestorationFromSeedVM: walletRestorationFromSeedVM);
|
||||
}
|
||||
|
||||
class RestoreFromSeedDetailsForm extends StatefulWidget {
|
||||
RestoreFromSeedDetailsForm({required this.walletRestorationFromSeedVM});
|
||||
|
||||
final WalletRestorationFromSeedVM walletRestorationFromSeedVM;
|
||||
|
||||
@override
|
||||
_RestoreFromSeedDetailsFormState createState() =>
|
||||
_RestoreFromSeedDetailsFormState();
|
||||
}
|
||||
|
||||
class _RestoreFromSeedDetailsFormState
|
||||
extends State<RestoreFromSeedDetailsForm> {
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
final _blockchainHeightKey = GlobalKey<BlockchainHeightState>();
|
||||
final _nameController = TextEditingController();
|
||||
ReactionDisposer? _stateReaction;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_stateReaction = reaction((_) => widget.walletRestorationFromSeedVM.state,
|
||||
(ExecutionState state) {
|
||||
if (state is ExecutedSuccessfullyState) {
|
||||
Navigator.of(context).popUntil((route) => route.isFirst);
|
||||
}
|
||||
|
||||
if (state is FailureState) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
showPopUp<void>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertWithOneAction(
|
||||
alertTitle: S.current.restore_title_from_seed,
|
||||
alertContent: state.error,
|
||||
buttonText: S.of(context).ok,
|
||||
buttonAction: () => Navigator.of(context).pop());
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
_nameController.addListener(
|
||||
() => widget.walletRestorationFromSeedVM.name = _nameController.text);
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_nameController.dispose();
|
||||
_stateReaction?.reaction.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
padding: EdgeInsets.only(left: 24, right: 24),
|
||||
child: ScrollableWithBottomSection(
|
||||
contentPadding: EdgeInsets.only(bottom: 24.0),
|
||||
content: Form(
|
||||
key: _formKey,
|
||||
child: Column(children: <Widget>[
|
||||
Row(
|
||||
children: <Widget>[
|
||||
Flexible(
|
||||
child: Container(
|
||||
padding: EdgeInsets.only(top: 20.0),
|
||||
child: BaseTextFormField(
|
||||
controller: _nameController,
|
||||
hintText: S.of(context).restore_wallet_name,
|
||||
validator: WalletNameValidator(),
|
||||
),
|
||||
))
|
||||
],
|
||||
),
|
||||
if (widget.walletRestorationFromSeedVM.hasRestorationHeight) ... [
|
||||
BlockchainHeightWidget(
|
||||
key: _blockchainHeightKey,
|
||||
onHeightChange: (height) {
|
||||
widget.walletRestorationFromSeedVM.height = height;
|
||||
print(height);
|
||||
}),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(left: 40, right: 40, top: 24),
|
||||
child: Text(
|
||||
S.of(context).restore_from_date_or_blockheight,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.normal,
|
||||
color: Theme.of(context).hintColor
|
||||
),
|
||||
),
|
||||
)
|
||||
]
|
||||
]),
|
||||
),
|
||||
bottomSectionPadding: EdgeInsets.only(bottom: 24),
|
||||
bottomSection: Observer(builder: (_) {
|
||||
return LoadingPrimaryButton(
|
||||
onPressed: () {
|
||||
if (_formKey.currentState != null && _formKey.currentState!.validate()) {
|
||||
widget.walletRestorationFromSeedVM.create();
|
||||
}
|
||||
},
|
||||
isLoading:
|
||||
widget.walletRestorationFromSeedVM.state is IsExecutingState,
|
||||
text: S.of(context).restore_recover,
|
||||
color: Theme.of(context).primaryColor,
|
||||
textColor: Colors.white,
|
||||
isDisabled: _nameController.text.isNotEmpty,
|
||||
);
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,30 +1,29 @@
|
|||
import 'package:cake_wallet/src/widgets/address_text_field.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_restore_view_model.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/widgets/blockchain_height_widget.dart';
|
||||
import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
|
||||
import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/core/wallet_name_validator.dart';
|
||||
import 'package:cake_wallet/entities/generate_name.dart';
|
||||
import 'package:cake_wallet/themes/extensions/send_page_theme.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
class WalletRestoreFromKeysFrom extends StatefulWidget {
|
||||
WalletRestoreFromKeysFrom({
|
||||
required this.walletRestoreViewModel,
|
||||
required this.displayPrivateKeyField,
|
||||
required this.onHeightOrDateEntered,
|
||||
Key? key,
|
||||
this.onHeightOrDateEntered,})
|
||||
: super(key: key);
|
||||
}) : super(key: key);
|
||||
|
||||
final Function(bool)? onHeightOrDateEntered;
|
||||
final Function(bool) onHeightOrDateEntered;
|
||||
final WalletRestoreViewModel walletRestoreViewModel;
|
||||
final bool displayPrivateKeyField;
|
||||
|
||||
@override
|
||||
WalletRestoreFromKeysFromState createState() =>
|
||||
WalletRestoreFromKeysFromState();
|
||||
WalletRestoreFromKeysFromState createState() => WalletRestoreFromKeysFromState();
|
||||
}
|
||||
|
||||
class WalletRestoreFromKeysFromState extends State<WalletRestoreFromKeysFrom> {
|
||||
|
@ -35,6 +34,7 @@ class WalletRestoreFromKeysFromState extends State<WalletRestoreFromKeysFrom> {
|
|||
addressController = TextEditingController(),
|
||||
viewKeyController = TextEditingController(),
|
||||
spendKeyController = TextEditingController(),
|
||||
privateKeyController = TextEditingController(),
|
||||
nameTextEditingController = TextEditingController();
|
||||
|
||||
final GlobalKey<FormState> formKey;
|
||||
|
@ -44,6 +44,18 @@ class WalletRestoreFromKeysFromState extends State<WalletRestoreFromKeysFrom> {
|
|||
final TextEditingController viewKeyController;
|
||||
final TextEditingController spendKeyController;
|
||||
final TextEditingController nameTextEditingController;
|
||||
final TextEditingController privateKeyController;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
privateKeyController.addListener(() {
|
||||
if (privateKeyController.text.isNotEmpty) {
|
||||
widget.onHeightOrDateEntered(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
|
@ -51,16 +63,18 @@ class WalletRestoreFromKeysFromState extends State<WalletRestoreFromKeysFrom> {
|
|||
addressController.dispose();
|
||||
viewKeyController.dispose();
|
||||
spendKeyController.dispose();
|
||||
privateKeyController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
padding: EdgeInsets.only(left: 24, right: 24),
|
||||
child: Form(
|
||||
key: formKey,
|
||||
child: Column(children: <Widget>[
|
||||
padding: EdgeInsets.only(left: 24, right: 24),
|
||||
child: Form(
|
||||
key: formKey,
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
Stack(
|
||||
alignment: Alignment.centerRight,
|
||||
children: [
|
||||
|
@ -75,9 +89,8 @@ class WalletRestoreFromKeysFromState extends State<WalletRestoreFromKeysFrom> {
|
|||
|
||||
setState(() {
|
||||
nameTextEditingController.text = rName;
|
||||
nameTextEditingController.selection =
|
||||
TextSelection.fromPosition(TextPosition(
|
||||
offset: nameTextEditingController.text.length));
|
||||
nameTextEditingController.selection = TextSelection.fromPosition(
|
||||
TextPosition(offset: nameTextEditingController.text.length));
|
||||
});
|
||||
},
|
||||
icon: Container(
|
||||
|
@ -90,7 +103,8 @@ class WalletRestoreFromKeysFromState extends State<WalletRestoreFromKeysFrom> {
|
|||
height: 34,
|
||||
child: Image.asset(
|
||||
'assets/images/refresh_icon.png',
|
||||
color: Theme.of(context).extension<SendPageTheme>()!.textFieldButtonIconColor,
|
||||
color:
|
||||
Theme.of(context).extension<SendPageTheme>()!.textFieldButtonIconColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -98,29 +112,65 @@ class WalletRestoreFromKeysFromState extends State<WalletRestoreFromKeysFrom> {
|
|||
],
|
||||
),
|
||||
Container(height: 20),
|
||||
BaseTextFormField(
|
||||
controller: addressController,
|
||||
keyboardType: TextInputType.multiline,
|
||||
maxLines: null,
|
||||
hintText: S.of(context).restore_address),
|
||||
Container(
|
||||
padding: EdgeInsets.only(top: 20.0),
|
||||
child: BaseTextFormField(
|
||||
controller: viewKeyController,
|
||||
hintText: S.of(context).restore_view_key_private,
|
||||
maxLines: null)),
|
||||
Container(
|
||||
padding: EdgeInsets.only(top: 20.0),
|
||||
child: BaseTextFormField(
|
||||
controller: spendKeyController,
|
||||
hintText: S.of(context).restore_spend_key_private,
|
||||
maxLines: null)),
|
||||
BlockchainHeightWidget(
|
||||
key: blockchainHeightKey,
|
||||
hasDatePicker: widget.walletRestoreViewModel.type != WalletType.haven,
|
||||
onHeightChange: (_) => null,
|
||||
onHeightOrDateEntered: widget.onHeightOrDateEntered)
|
||||
]),
|
||||
));
|
||||
_restoreFromKeysFormFields(),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _restoreFromKeysFormFields() {
|
||||
if (widget.displayPrivateKeyField) {
|
||||
return AddressTextField(
|
||||
controller: privateKeyController,
|
||||
placeholder: S.of(context).private_key,
|
||||
options: [AddressTextFieldOption.paste],
|
||||
buttonColor: Theme.of(context).hintColor,
|
||||
onPushPasteButton: (_) {
|
||||
_pasteText();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
BaseTextFormField(
|
||||
controller: addressController,
|
||||
keyboardType: TextInputType.multiline,
|
||||
maxLines: null,
|
||||
hintText: S.of(context).restore_address,
|
||||
),
|
||||
Container(
|
||||
padding: EdgeInsets.only(top: 20.0),
|
||||
child: BaseTextFormField(
|
||||
controller: viewKeyController,
|
||||
hintText: S.of(context).restore_view_key_private,
|
||||
maxLines: null,
|
||||
),
|
||||
),
|
||||
Container(
|
||||
padding: EdgeInsets.only(top: 20.0),
|
||||
child: BaseTextFormField(
|
||||
controller: spendKeyController,
|
||||
hintText: S.of(context).restore_spend_key_private,
|
||||
maxLines: null,
|
||||
),
|
||||
),
|
||||
BlockchainHeightWidget(
|
||||
key: blockchainHeightKey,
|
||||
hasDatePicker: widget.walletRestoreViewModel.type != WalletType.haven,
|
||||
onHeightChange: (_) => null,
|
||||
onHeightOrDateEntered: widget.onHeightOrDateEntered,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _pasteText() async {
|
||||
final value = await Clipboard.getData('text/plain');
|
||||
|
||||
if (value?.text?.isNotEmpty ?? false) {
|
||||
privateKeyController.text = value!.text!;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,7 @@
|
|||
import 'package:cake_wallet/themes/extensions/keyboard_theme.dart';
|
||||
import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
|
||||
import 'package:cake_wallet/src/widgets/keyboard_done_button.dart';
|
||||
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
|
||||
import 'package:cake_wallet/utils/responsive_layout_util.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:keyboard_actions/keyboard_actions.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
|
@ -19,10 +16,6 @@ import 'package:cake_wallet/src/screens/restore/wallet_restore_from_keys_form.da
|
|||
import 'package:cake_wallet/src/screens/restore/wallet_restore_from_seed_form.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||
import 'package:cake_wallet/core/validator.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
|
||||
import 'package:cake_wallet/core/seed_validator.dart';
|
||||
import 'package:cake_wallet/view_model/restore/restore_mode.dart';
|
||||
import 'package:cake_wallet/themes/extensions/wallet_list_theme.dart';
|
||||
|
||||
|
@ -76,6 +69,7 @@ class WalletRestorePage extends BasePage {
|
|||
_pages.add(WalletRestoreFromKeysFrom(
|
||||
key: walletRestoreFromKeysFormKey,
|
||||
walletRestoreViewModel: walletRestoreViewModel,
|
||||
displayPrivateKeyField: walletRestoreViewModel.hasRestoreFromPrivateKey,
|
||||
onHeightOrDateEntered: (value) => walletRestoreViewModel.isButtonEnabled = value));
|
||||
break;
|
||||
default:
|
||||
|
@ -193,8 +187,12 @@ class WalletRestorePage extends BasePage {
|
|||
return LoadingPrimaryButton(
|
||||
onPressed: _confirmForm,
|
||||
text: S.of(context).restore_recover,
|
||||
color: Theme.of(context).extension<WalletListTheme>()!.createNewWalletButtonBackgroundColor,
|
||||
textColor: Theme.of(context).extension<WalletListTheme>()!.restoreWalletButtonTextColor,
|
||||
color: Theme.of(context)
|
||||
.extension<WalletListTheme>()!
|
||||
.createNewWalletButtonBackgroundColor,
|
||||
textColor: Theme.of(context)
|
||||
.extension<WalletListTheme>()!
|
||||
.restoreWalletButtonTextColor,
|
||||
isLoading: walletRestoreViewModel.state is IsExecutingState,
|
||||
isDisabled: !walletRestoreViewModel.isButtonEnabled,
|
||||
);
|
||||
|
@ -246,11 +244,18 @@ class WalletRestorePage extends BasePage {
|
|||
credentials['name'] =
|
||||
walletRestoreFromSeedFormKey.currentState!.nameTextEditingController.text;
|
||||
} else {
|
||||
credentials['address'] = walletRestoreFromKeysFormKey.currentState!.addressController.text;
|
||||
credentials['viewKey'] = walletRestoreFromKeysFormKey.currentState!.viewKeyController.text;
|
||||
credentials['spendKey'] = walletRestoreFromKeysFormKey.currentState!.spendKeyController.text;
|
||||
credentials['height'] =
|
||||
walletRestoreFromKeysFormKey.currentState!.blockchainHeightKey.currentState!.height;
|
||||
if (walletRestoreViewModel.hasRestoreFromPrivateKey) {
|
||||
credentials['private_key'] =
|
||||
walletRestoreFromKeysFormKey.currentState!.privateKeyController.text;
|
||||
} else {
|
||||
credentials['address'] = walletRestoreFromKeysFormKey.currentState!.addressController.text;
|
||||
credentials['viewKey'] = walletRestoreFromKeysFormKey.currentState!.viewKeyController.text;
|
||||
credentials['spendKey'] =
|
||||
walletRestoreFromKeysFormKey.currentState!.spendKeyController.text;
|
||||
credentials['height'] =
|
||||
walletRestoreFromKeysFormKey.currentState!.blockchainHeightKey.currentState!.height;
|
||||
}
|
||||
|
||||
credentials['name'] =
|
||||
walletRestoreFromKeysFormKey.currentState!.nameTextEditingController.text;
|
||||
}
|
||||
|
|
0
lib/src/screens/restore/widgets/backup_file_button.dart
Normal file
0
lib/src/screens/restore/widgets/backup_file_button.dart
Normal file
|
@ -100,7 +100,7 @@ class SendPage extends BasePage {
|
|||
AppBarStyle get appBarStyle => AppBarStyle.transparent;
|
||||
|
||||
double _sendCardHeight(BuildContext context) {
|
||||
final double initialHeight = sendViewModel.isElectrumWallet ? 490 : 465;
|
||||
final double initialHeight = sendViewModel.hasCoinControl ? 490 : 465;
|
||||
|
||||
if (!ResponsiveLayoutUtil.instance.isMobile) {
|
||||
return initialHeight - 66;
|
||||
|
|
|
@ -496,7 +496,7 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
|
|||
),
|
||||
),
|
||||
),
|
||||
if (sendViewModel.isElectrumWallet)
|
||||
if (sendViewModel.hasCoinControl)
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 6),
|
||||
child: GestureDetector(
|
||||
|
|
|
@ -42,36 +42,40 @@ class ManageNodesPage extends BasePage {
|
|||
return nodeListViewModel.nodes.length;
|
||||
},
|
||||
itemBuilder: (_, index) {
|
||||
final node = nodeListViewModel.nodes[index];
|
||||
final isSelected = node.keyIndex == nodeListViewModel.currentNode.keyIndex;
|
||||
final nodeListRow = NodeListRow(
|
||||
title: node.uriRaw,
|
||||
node: node,
|
||||
isSelected: isSelected,
|
||||
onTap: (_) async {
|
||||
if (isSelected) {
|
||||
return;
|
||||
}
|
||||
return Observer(
|
||||
builder: (context) {
|
||||
final node = nodeListViewModel.nodes[index];
|
||||
final isSelected = node.keyIndex == nodeListViewModel.currentNode.keyIndex;
|
||||
final nodeListRow = NodeListRow(
|
||||
title: node.uriRaw,
|
||||
node: node,
|
||||
isSelected: isSelected,
|
||||
onTap: (_) async {
|
||||
if (isSelected) {
|
||||
return;
|
||||
}
|
||||
|
||||
await showPopUp<void>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertWithTwoActions(
|
||||
alertTitle: S.of(context).change_current_node_title,
|
||||
alertContent: nodeListViewModel.getAlertContent(node.uriRaw),
|
||||
leftButtonText: S.of(context).cancel,
|
||||
rightButtonText: S.of(context).change,
|
||||
actionLeftButton: () => Navigator.of(context).pop(),
|
||||
actionRightButton: () async {
|
||||
await nodeListViewModel.setAsCurrent(node);
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
);
|
||||
});
|
||||
await showPopUp<void>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertWithTwoActions(
|
||||
alertTitle: S.of(context).change_current_node_title,
|
||||
alertContent: nodeListViewModel.getAlertContent(node.uriRaw),
|
||||
leftButtonText: S.of(context).cancel,
|
||||
rightButtonText: S.of(context).change,
|
||||
actionLeftButton: () => Navigator.of(context).pop(),
|
||||
actionRightButton: () async {
|
||||
await nodeListViewModel.setAsCurrent(node);
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
return nodeListRow;
|
||||
},
|
||||
);
|
||||
|
||||
return nodeListRow;
|
||||
},
|
||||
),
|
||||
);
|
||||
|
|
|
@ -50,6 +50,14 @@ class PrivacyPage extends BasePage {
|
|||
onValueChange: (BuildContext _, bool value) {
|
||||
_privacySettingsViewModel.setShouldSaveRecipientAddress(value);
|
||||
}),
|
||||
if (_privacySettingsViewModel.isAutoGenerateSubaddressesVisible)
|
||||
SettingsSwitcherCell(
|
||||
title: S.current.auto_generate_subaddresses,
|
||||
value: _privacySettingsViewModel.isAutoGenerateSubaddressesEnabled,
|
||||
onValueChange: (BuildContext _, bool value) {
|
||||
_privacySettingsViewModel.setAutoGenerateSubaddresses(value);
|
||||
},
|
||||
),
|
||||
if (DeviceInfo.instance.isMobile)
|
||||
SettingsSwitcherCell(
|
||||
title: S.current.prevent_screenshots,
|
||||
|
|
|
@ -21,7 +21,6 @@ class TransactionDetailsPage extends BasePage {
|
|||
|
||||
@override
|
||||
Widget body(BuildContext context) {
|
||||
// FIX-ME: Added `context` it was not used here before, maby bug ?
|
||||
return SectionStandardList(
|
||||
sectionCount: 1,
|
||||
itemCounter: (int _) => transactionDetailsViewModel.items.length,
|
||||
|
|
|
@ -6,11 +6,12 @@ import 'package:cake_wallet/themes/extensions/transaction_trade_theme.dart';
|
|||
class TextFieldListRow extends StatelessWidget {
|
||||
TextFieldListRow(
|
||||
{required this.title,
|
||||
required this.value,
|
||||
this.titleFontSize = 14,
|
||||
this.valueFontSize = 16,
|
||||
this.onSubmitted})
|
||||
: _textController = TextEditingController() {
|
||||
required this.value,
|
||||
this.titleFontSize = 14,
|
||||
this.valueFontSize = 16,
|
||||
this.onSubmitted,
|
||||
this.onTapOutside})
|
||||
: _textController = TextEditingController() {
|
||||
_textController.text = value;
|
||||
}
|
||||
|
||||
|
@ -19,6 +20,7 @@ class TextFieldListRow extends StatelessWidget {
|
|||
final double titleFontSize;
|
||||
final double valueFontSize;
|
||||
final Function(String value)? onSubmitted;
|
||||
final Function(String value)? onTapOutside;
|
||||
final TextEditingController _textController;
|
||||
|
||||
@override
|
||||
|
@ -58,6 +60,7 @@ class TextFieldListRow extends StatelessWidget {
|
|||
fontWeight: FontWeight.w500,
|
||||
color: Theme.of(context).extension<TransactionTradeTheme>()!.detailsTitlesColor),
|
||||
border: InputBorder.none),
|
||||
onTapOutside: (_) => onTapOutside?.call(_textController.text),
|
||||
onSubmitted: (value) => onSubmitted?.call(value),
|
||||
)
|
||||
]),
|
||||
|
|
|
@ -24,7 +24,6 @@ class UnspentCoinsDetailsPage extends BasePage {
|
|||
|
||||
@override
|
||||
Widget body(BuildContext context) {
|
||||
// FIX-ME: Added `context` it was not used here before, maby bug ?
|
||||
return SectionStandardList(
|
||||
sectionCount: 1,
|
||||
itemCounter: (int _) => unspentCoinsDetailsViewModel.items.length,
|
||||
|
@ -45,6 +44,7 @@ class UnspentCoinsDetailsPage extends BasePage {
|
|||
return TextFieldListRow(
|
||||
title: item.title,
|
||||
value: item.value,
|
||||
onTapOutside: item.onSubmitted,
|
||||
onSubmitted: item.onSubmitted,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -94,7 +94,7 @@ class UnspentCoinsListItem extends StatelessWidget {
|
|||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
AutoSizeText(
|
||||
address,
|
||||
'${address.substring(0, 5)}...${address.substring(address.length-5)}', // ToDo: Maybe use address label
|
||||
style: TextStyle(
|
||||
color: addressColor,
|
||||
fontSize: 12,
|
||||
|
|
|
@ -17,24 +17,21 @@ class SearchBarWidget extends StatelessWidget {
|
|||
Widget build(BuildContext context) {
|
||||
return TextFormField(
|
||||
controller: searchController,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).extension<PickerTheme>()!.searchTextColor),
|
||||
style: TextStyle(color: Theme.of(context).extension<PickerTheme>()!.searchTextColor),
|
||||
decoration: InputDecoration(
|
||||
hintText: hintText ?? S.of(context).search_currency,
|
||||
hintStyle: TextStyle(
|
||||
color: Theme.of(context).extension<PickerTheme>()!.searchHintColor),
|
||||
hintStyle: TextStyle(color: Theme.of(context).extension<PickerTheme>()!.searchHintColor),
|
||||
prefixIcon: Image.asset("assets/images/search_icon.png",
|
||||
color: Theme.of(context).extension<PickerTheme>()!.searchIconColor),
|
||||
filled: true,
|
||||
fillColor: Theme.of(context)
|
||||
.extension<PickerTheme>()!
|
||||
.searchBackgroundFillColor,
|
||||
fillColor: Theme.of(context).extension<PickerTheme>()!.searchBackgroundFillColor,
|
||||
alignLabelWithHint: false,
|
||||
contentPadding: const EdgeInsets.symmetric(vertical: 4, horizontal: 16),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(borderRadius),
|
||||
borderSide: const BorderSide(
|
||||
color: Colors.transparent,
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).extension<PickerTheme>()!.searchBorderColor ??
|
||||
Colors.transparent,
|
||||
)),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(borderRadius),
|
||||
|
|
|
@ -1,16 +1,9 @@
|
|||
import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:cake_wallet/src/widgets/validable_annotated_editable_text.dart';
|
||||
import 'package:cake_wallet/src/widgets/blockchain_height_widget.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:cake_wallet/palette.dart';
|
||||
import 'package:cake_wallet/core/seed_validator.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:cake_wallet/entities/mnemonic_item.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:cake_wallet/themes/extensions/send_page_theme.dart';
|
||||
|
||||
class SeedWidget extends StatefulWidget {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import 'dart:io';
|
||||
|
||||
import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
||||
import 'package:cake_wallet/entities/auto_generate_subaddress_status.dart';
|
||||
import 'package:cake_wallet/entities/cake_2fa_preset_options.dart';
|
||||
import 'package:cake_wallet/entities/background_tasks.dart';
|
||||
import 'package:cake_wallet/entities/exchange_api_mode.dart';
|
||||
|
@ -42,6 +43,7 @@ abstract class SettingsStoreBase with Store {
|
|||
required FiatCurrency initialFiatCurrency,
|
||||
required BalanceDisplayMode initialBalanceDisplayMode,
|
||||
required bool initialSaveRecipientAddress,
|
||||
required AutoGenerateSubaddressStatus initialAutoGenerateSubaddressStatus,
|
||||
required bool initialAppSecure,
|
||||
required bool initialDisableBuy,
|
||||
required bool initialDisableSell,
|
||||
|
@ -87,6 +89,7 @@ abstract class SettingsStoreBase with Store {
|
|||
fiatCurrency = initialFiatCurrency,
|
||||
balanceDisplayMode = initialBalanceDisplayMode,
|
||||
shouldSaveRecipientAddress = initialSaveRecipientAddress,
|
||||
autoGenerateSubaddressStatus = initialAutoGenerateSubaddressStatus,
|
||||
fiatApiMode = initialFiatMode,
|
||||
allowBiometricalAuthentication = initialAllowBiometricalAuthentication,
|
||||
selectedCake2FAPreset = initialCake2FAPresetOptions,
|
||||
|
@ -197,6 +200,11 @@ abstract class SettingsStoreBase with Store {
|
|||
(bool disableSell) =>
|
||||
sharedPreferences.setBool(PreferencesKey.disableSellKey, disableSell));
|
||||
|
||||
reaction(
|
||||
(_) => autoGenerateSubaddressStatus,
|
||||
(AutoGenerateSubaddressStatus autoGenerateSubaddressStatus) => sharedPreferences.setInt(
|
||||
PreferencesKey.autoGenerateSubaddressStatusKey, autoGenerateSubaddressStatus.value));
|
||||
|
||||
reaction(
|
||||
(_) => fiatApiMode,
|
||||
(FiatApiMode mode) =>
|
||||
|
@ -337,6 +345,7 @@ abstract class SettingsStoreBase with Store {
|
|||
static const defaultPinLength = 4;
|
||||
static const defaultActionsMode = 11;
|
||||
static const defaultPinCodeTimeOutDuration = PinCodeRequiredDuration.tenminutes;
|
||||
static const defaultAutoGenerateSubaddressStatus = AutoGenerateSubaddressStatus.initialized;
|
||||
|
||||
@observable
|
||||
FiatCurrency fiatCurrency;
|
||||
|
@ -359,6 +368,9 @@ abstract class SettingsStoreBase with Store {
|
|||
@observable
|
||||
bool shouldSaveRecipientAddress;
|
||||
|
||||
@observable
|
||||
AutoGenerateSubaddressStatus autoGenerateSubaddressStatus;
|
||||
|
||||
@observable
|
||||
bool isAppSecure;
|
||||
|
||||
|
@ -602,7 +614,12 @@ abstract class SettingsStoreBase with Store {
|
|||
final packageInfo = await PackageInfo.fromPlatform();
|
||||
final deviceName = await _getDeviceName() ?? '';
|
||||
final shouldShowYatPopup = sharedPreferences.getBool(PreferencesKey.shouldShowYatPopup) ?? true;
|
||||
final generateSubaddresses =
|
||||
sharedPreferences.getInt(PreferencesKey.autoGenerateSubaddressStatusKey);
|
||||
|
||||
final autoGenerateSubaddressStatus = generateSubaddresses != null
|
||||
? AutoGenerateSubaddressStatus.deserialize(raw: generateSubaddresses)
|
||||
: defaultAutoGenerateSubaddressStatus;
|
||||
final nodes = <WalletType, Node>{};
|
||||
|
||||
if (moneroNode != null) {
|
||||
|
@ -640,6 +657,7 @@ abstract class SettingsStoreBase with Store {
|
|||
initialFiatCurrency: currentFiatCurrency,
|
||||
initialBalanceDisplayMode: currentBalanceDisplayMode,
|
||||
initialSaveRecipientAddress: shouldSaveRecipientAddress,
|
||||
initialAutoGenerateSubaddressStatus: autoGenerateSubaddressStatus,
|
||||
initialAppSecure: isAppSecure,
|
||||
initialDisableBuy: disableBuy,
|
||||
initialDisableSell: disableSell,
|
||||
|
@ -709,6 +727,13 @@ abstract class SettingsStoreBase with Store {
|
|||
priority[WalletType.ethereum]!;
|
||||
}
|
||||
|
||||
final generateSubaddresses =
|
||||
sharedPreferences.getInt(PreferencesKey.autoGenerateSubaddressStatusKey);
|
||||
|
||||
autoGenerateSubaddressStatus = generateSubaddresses != null
|
||||
? AutoGenerateSubaddressStatus.deserialize(raw: generateSubaddresses)
|
||||
: defaultAutoGenerateSubaddressStatus;
|
||||
|
||||
balanceDisplayMode = BalanceDisplayMode.deserialize(
|
||||
raw: sharedPreferences.getInt(PreferencesKey.currentBalanceDisplayModeKey)!);
|
||||
shouldSaveRecipientAddress =
|
||||
|
@ -719,8 +744,6 @@ abstract class SettingsStoreBase with Store {
|
|||
|
||||
numberOfFailedTokenTrials =
|
||||
sharedPreferences.getInt(PreferencesKey.failedTotpTokenTrials) ?? numberOfFailedTokenTrials;
|
||||
sharedPreferences.getBool(PreferencesKey.shouldSaveRecipientAddressKey) ??
|
||||
shouldSaveRecipientAddress;
|
||||
isAppSecure = sharedPreferences.getBool(PreferencesKey.isAppSecureKey) ?? isAppSecure;
|
||||
disableBuy = sharedPreferences.getBool(PreferencesKey.disableBuyKey) ?? disableBuy;
|
||||
disableSell = sharedPreferences.getBool(PreferencesKey.disableSellKey) ?? disableSell;
|
||||
|
|
|
@ -6,13 +6,15 @@ class PickerTheme extends ThemeExtension<PickerTheme> {
|
|||
final Color searchBackgroundFillColor;
|
||||
final Color searchTextColor;
|
||||
final Color? searchHintColor;
|
||||
final Color? searchBorderColor;
|
||||
|
||||
PickerTheme(
|
||||
{required this.dividerColor,
|
||||
this.searchIconColor,
|
||||
required this.searchBackgroundFillColor,
|
||||
required this.searchTextColor,
|
||||
this.searchHintColor});
|
||||
this.searchHintColor,
|
||||
this.searchBorderColor});
|
||||
|
||||
@override
|
||||
PickerTheme copyWith(
|
||||
|
@ -20,14 +22,15 @@ class PickerTheme extends ThemeExtension<PickerTheme> {
|
|||
Color? searchIconColor,
|
||||
Color? searchBackgroundFillColor,
|
||||
Color? searchTextColor,
|
||||
Color? searchHintColor}) =>
|
||||
Color? searchHintColor,
|
||||
Color? searchBorderColor}) =>
|
||||
PickerTheme(
|
||||
dividerColor: dividerColor ?? this.dividerColor,
|
||||
searchIconColor: searchIconColor ?? this.searchIconColor,
|
||||
searchBackgroundFillColor:
|
||||
searchBackgroundFillColor ?? this.searchBackgroundFillColor,
|
||||
searchBackgroundFillColor: searchBackgroundFillColor ?? this.searchBackgroundFillColor,
|
||||
searchTextColor: searchTextColor ?? this.searchTextColor,
|
||||
searchHintColor: searchHintColor ?? this.searchHintColor);
|
||||
searchHintColor: searchHintColor ?? this.searchHintColor,
|
||||
searchBorderColor: searchBorderColor ?? this.searchBorderColor);
|
||||
|
||||
@override
|
||||
PickerTheme lerp(ThemeExtension<PickerTheme>? other, double t) {
|
||||
|
@ -36,19 +39,14 @@ class PickerTheme extends ThemeExtension<PickerTheme> {
|
|||
}
|
||||
|
||||
return PickerTheme(
|
||||
dividerColor:
|
||||
Color.lerp(dividerColor, other.dividerColor, t) ?? dividerColor,
|
||||
searchIconColor:
|
||||
Color.lerp(searchIconColor, other.searchIconColor, t) ??
|
||||
searchIconColor,
|
||||
searchBackgroundFillColor: Color.lerp(searchBackgroundFillColor,
|
||||
other.searchBackgroundFillColor, t) ??
|
||||
searchBackgroundFillColor,
|
||||
searchTextColor:
|
||||
Color.lerp(searchTextColor, other.searchTextColor, t) ??
|
||||
searchTextColor,
|
||||
searchHintColor:
|
||||
Color.lerp(searchHintColor, other.searchHintColor, t) ??
|
||||
searchHintColor);
|
||||
dividerColor: Color.lerp(dividerColor, other.dividerColor, t) ?? dividerColor,
|
||||
searchIconColor: Color.lerp(searchIconColor, other.searchIconColor, t) ?? searchIconColor,
|
||||
searchBackgroundFillColor:
|
||||
Color.lerp(searchBackgroundFillColor, other.searchBackgroundFillColor, t) ??
|
||||
searchBackgroundFillColor,
|
||||
searchTextColor: Color.lerp(searchTextColor, other.searchTextColor, t) ?? searchTextColor,
|
||||
searchHintColor: Color.lerp(searchHintColor, other.searchHintColor, t) ?? searchHintColor,
|
||||
searchBorderColor:
|
||||
Color.lerp(searchBorderColor, other.searchBorderColor, t) ?? searchBorderColor);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,14 +39,12 @@ class HighContrastTheme extends MoneroLightTheme {
|
|||
|
||||
@override
|
||||
CakeTextTheme get cakeTextTheme => super.cakeTextTheme.copyWith(
|
||||
buttonTextColor: Colors.white,
|
||||
buttonSecondaryTextColor: Colors.white.withOpacity(0.5));
|
||||
buttonTextColor: Colors.white, buttonSecondaryTextColor: Colors.white.withOpacity(0.5));
|
||||
|
||||
@override
|
||||
SyncIndicatorTheme get syncIndicatorStyle =>
|
||||
super.syncIndicatorStyle.copyWith(
|
||||
textColor: colorScheme.background,
|
||||
syncedBackgroundColor: containerColor);
|
||||
SyncIndicatorTheme get syncIndicatorStyle => super
|
||||
.syncIndicatorStyle
|
||||
.copyWith(textColor: colorScheme.background, syncedBackgroundColor: containerColor);
|
||||
|
||||
@override
|
||||
BalancePageTheme get balancePageTheme => super.balancePageTheme.copyWith(
|
||||
|
@ -56,32 +54,28 @@ class HighContrastTheme extends MoneroLightTheme {
|
|||
balanceAmountColor: Colors.white);
|
||||
|
||||
@override
|
||||
DashboardPageTheme get dashboardPageTheme =>
|
||||
super.dashboardPageTheme.copyWith(
|
||||
textColor: Colors.black,
|
||||
cardTextColor: Colors.white,
|
||||
mainActionsIconColor: Colors.white,
|
||||
indicatorDotTheme: IndicatorDotTheme(
|
||||
indicatorColor: Colors.grey, activeIndicatorColor: Colors.black));
|
||||
DashboardPageTheme get dashboardPageTheme => super.dashboardPageTheme.copyWith(
|
||||
textColor: Colors.black,
|
||||
cardTextColor: Colors.white,
|
||||
mainActionsIconColor: Colors.white,
|
||||
indicatorDotTheme:
|
||||
IndicatorDotTheme(indicatorColor: Colors.grey, activeIndicatorColor: Colors.black));
|
||||
|
||||
@override
|
||||
ExchangePageTheme get exchangePageTheme => super
|
||||
.exchangePageTheme
|
||||
.copyWith(firstGradientTopPanelColor: containerColor);
|
||||
ExchangePageTheme get exchangePageTheme => super.exchangePageTheme.copyWith(
|
||||
firstGradientTopPanelColor: primaryColor, firstGradientBottomPanelColor: containerColor);
|
||||
|
||||
@override
|
||||
SendPageTheme get sendPageTheme => super.sendPageTheme.copyWith(
|
||||
templateTitleColor: Colors.white,
|
||||
templateBackgroundColor: Colors.black,
|
||||
firstGradientColor: containerColor);
|
||||
firstGradientColor: primaryColor);
|
||||
|
||||
@override
|
||||
AddressTheme get addressTheme =>
|
||||
super.addressTheme.copyWith(actionButtonColor: Colors.grey);
|
||||
AddressTheme get addressTheme => super.addressTheme.copyWith(actionButtonColor: Colors.grey);
|
||||
|
||||
@override
|
||||
FilterTheme get filterTheme =>
|
||||
super.filterTheme.copyWith(iconColor: Colors.white);
|
||||
FilterTheme get filterTheme => super.filterTheme.copyWith(iconColor: Colors.white);
|
||||
|
||||
@override
|
||||
CakeMenuTheme get menuTheme => super.menuTheme.copyWith(
|
||||
|
@ -91,10 +85,11 @@ class HighContrastTheme extends MoneroLightTheme {
|
|||
|
||||
@override
|
||||
PickerTheme get pickerTheme => super.pickerTheme.copyWith(
|
||||
searchIconColor: Colors.white,
|
||||
searchHintColor: Colors.white,
|
||||
searchTextColor: Colors.white,
|
||||
searchBackgroundFillColor: Colors.grey);
|
||||
searchIconColor: primaryColor,
|
||||
searchHintColor: primaryColor,
|
||||
searchTextColor: primaryColor,
|
||||
searchBackgroundFillColor: Colors.white,
|
||||
searchBorderColor: primaryColor);
|
||||
|
||||
@override
|
||||
AccountListTheme get accountListTheme => super.accountListTheme.copyWith(
|
||||
|
@ -106,13 +101,10 @@ class HighContrastTheme extends MoneroLightTheme {
|
|||
|
||||
@override
|
||||
ReceivePageTheme get receivePageTheme => super.receivePageTheme.copyWith(
|
||||
tilesTextColor: Colors.white,
|
||||
iconsBackgroundColor: Colors.grey,
|
||||
iconsColor: Colors.black);
|
||||
tilesTextColor: Colors.white, iconsBackgroundColor: Colors.grey, iconsColor: Colors.black);
|
||||
|
||||
@override
|
||||
ThemeData get themeData => super.themeData.copyWith(
|
||||
disabledColor: Colors.grey,
|
||||
dialogTheme:
|
||||
super.themeData.dialogTheme.copyWith(backgroundColor: Colors.white));
|
||||
dialogTheme: super.themeData.dialogTheme.copyWith(backgroundColor: Colors.white));
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ import 'package:cake_wallet/themes/extensions/transaction_trade_theme.dart';
|
|||
import 'package:cake_wallet/themes/extensions/wallet_list_theme.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
enum ThemeType { bright, light, dark }
|
||||
enum ThemeType { light, bright, dark }
|
||||
|
||||
abstract class ThemeBase {
|
||||
ThemeBase({required this.raw}) {
|
||||
|
|
|
@ -148,6 +148,7 @@ class ExceptionHandler {
|
|||
"CERTIFICATE_VERIFY_FAILED",
|
||||
"Handshake error in client",
|
||||
"Error while launching http",
|
||||
"OS Error: Network is unreachable",
|
||||
];
|
||||
|
||||
static Future<void> _addDeviceInfo(File file) async {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import 'dart:async';
|
||||
import 'package:cake_wallet/entities/auto_generate_subaddress_status.dart';
|
||||
import 'package:cake_wallet/entities/contact_base.dart';
|
||||
import 'package:cake_wallet/entities/wallet_contact.dart';
|
||||
import 'package:cake_wallet/store/settings_store.dart';
|
||||
|
@ -10,6 +11,7 @@ import 'package:cake_wallet/entities/contact_record.dart';
|
|||
import 'package:cake_wallet/entities/contact.dart';
|
||||
import 'package:cake_wallet/utils/mobx.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
|
||||
part 'contact_list_view_model.g.dart';
|
||||
|
||||
|
@ -20,12 +22,26 @@ abstract class ContactListViewModelBase with Store {
|
|||
ContactListViewModelBase(this.contactSource, this.walletInfoSource,
|
||||
this._currency, this.settingsStore)
|
||||
: contacts = ObservableList<ContactRecord>(),
|
||||
walletContacts = [] {
|
||||
walletContacts = [],
|
||||
isAutoGenerateEnabled =
|
||||
settingsStore.autoGenerateSubaddressStatus == AutoGenerateSubaddressStatus.enabled {
|
||||
walletInfoSource.values.forEach((info) {
|
||||
if (info.addresses?.isNotEmpty ?? false) {
|
||||
info.addresses?.forEach((address, label) {
|
||||
final name = label.isNotEmpty ? info.name + ' ($label)' : info.name;
|
||||
|
||||
if (isAutoGenerateEnabled && info.type == WalletType.monero && info.addressInfos != null) {
|
||||
info.addressInfos!.forEach((key, value) {
|
||||
final nextUnusedAddress = value.firstWhereOrNull(
|
||||
(addressInfo) => !(info.usedAddresses?.contains(addressInfo.address) ?? false));
|
||||
if (nextUnusedAddress != null) {
|
||||
final name = _createName(info.name, nextUnusedAddress.label);
|
||||
walletContacts.add(WalletContact(
|
||||
nextUnusedAddress.address,
|
||||
name,
|
||||
walletTypeToCryptoCurrency(info.type),
|
||||
));
|
||||
}
|
||||
});
|
||||
} else if (info.addresses?.isNotEmpty == true) {
|
||||
info.addresses!.forEach((address, label) {
|
||||
final name = _createName(info.name, label);
|
||||
walletContacts.add(WalletContact(
|
||||
address,
|
||||
name,
|
||||
|
@ -40,6 +56,11 @@ abstract class ContactListViewModelBase with Store {
|
|||
initialFire: true);
|
||||
}
|
||||
|
||||
String _createName(String walletName, String label) {
|
||||
return label.isNotEmpty ? '$walletName ($label)' : walletName;
|
||||
}
|
||||
|
||||
final bool isAutoGenerateEnabled;
|
||||
final Box<Contact> contactSource;
|
||||
final Box<WalletInfo> walletInfoSource;
|
||||
final ObservableList<ContactRecord> contacts;
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import 'package:cake_wallet/entities/auto_generate_subaddress_status.dart';
|
||||
import 'package:cake_wallet/entities/exchange_api_mode.dart';
|
||||
import 'package:cake_wallet/store/anonpay/anonpay_transactions_store.dart';
|
||||
import 'package:cake_wallet/view_model/dashboard/anonpay_transaction_list_item.dart';
|
||||
|
@ -107,7 +108,7 @@ abstract class DashboardViewModelBase with Store {
|
|||
name = wallet.name;
|
||||
type = wallet.type;
|
||||
isOutdatedElectrumWallet =
|
||||
wallet.type == WalletType.bitcoin && wallet.seed.split(' ').length < 24;
|
||||
wallet.type == WalletType.bitcoin && wallet.seed!.split(' ').length < 24;
|
||||
isShowFirstYatIntroduction = false;
|
||||
isShowSecondYatIntroduction = false;
|
||||
isShowThirdYatIntroduction = false;
|
||||
|
@ -235,6 +236,10 @@ abstract class DashboardViewModelBase with Store {
|
|||
@computed
|
||||
double get price => balanceViewModel.price;
|
||||
|
||||
@computed
|
||||
bool get isAutoGenerateSubaddressesEnabled =>
|
||||
settingsStore.autoGenerateSubaddressStatus != AutoGenerateSubaddressStatus.disabled;
|
||||
|
||||
@computed
|
||||
List<ActionListItem> get items {
|
||||
final _items = <ActionListItem>[];
|
||||
|
@ -327,7 +332,7 @@ abstract class DashboardViewModelBase with Store {
|
|||
type = wallet.type;
|
||||
name = wallet.name;
|
||||
isOutdatedElectrumWallet =
|
||||
wallet.type == WalletType.bitcoin && wallet.seed.split(' ').length < 24;
|
||||
wallet.type == WalletType.bitcoin && wallet.seed!.split(' ').length < 24;
|
||||
updateActions();
|
||||
|
||||
if (wallet.type == WalletType.monero) {
|
||||
|
|
|
@ -2,6 +2,7 @@ import 'dart:async';
|
|||
import 'dart:collection';
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:cake_wallet/core/wallet_change_listener_view_model.dart';
|
||||
import 'package:cake_wallet/entities/exchange_api_mode.dart';
|
||||
import 'package:cake_wallet/entities/preferences_key.dart';
|
||||
import 'package:cake_wallet/entities/wallet_contact.dart';
|
||||
|
@ -13,7 +14,7 @@ import 'package:cake_wallet/exchange/trocador/trocador_exchange_provider.dart';
|
|||
import 'package:cake_wallet/exchange/trocador/trocador_request.dart';
|
||||
import 'package:cake_wallet/view_model/contact_list/contact_list_view_model.dart';
|
||||
import 'package:cw_core/transaction_priority.dart';
|
||||
import 'package:cw_core/wallet_base.dart';
|
||||
import 'package:cake_wallet/store/app_store.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/sync_status.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
|
@ -45,16 +46,22 @@ part 'exchange_view_model.g.dart';
|
|||
|
||||
class ExchangeViewModel = ExchangeViewModelBase with _$ExchangeViewModel;
|
||||
|
||||
abstract class ExchangeViewModelBase with Store {
|
||||
abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with Store {
|
||||
@override
|
||||
void onWalletChange(wallet) {
|
||||
receiveCurrency = wallet.currency;
|
||||
depositCurrency = wallet.currency;
|
||||
}
|
||||
|
||||
ExchangeViewModelBase(
|
||||
this.wallet,
|
||||
this.trades,
|
||||
this._exchangeTemplateStore,
|
||||
this.tradesStore,
|
||||
this._settingsStore,
|
||||
this.sharedPreferences,
|
||||
this.contactListViewModel)
|
||||
: _cryptoNumberFormat = NumberFormat(),
|
||||
AppStore appStore,
|
||||
this.trades,
|
||||
this._exchangeTemplateStore,
|
||||
this.tradesStore,
|
||||
this._settingsStore,
|
||||
this.sharedPreferences,
|
||||
this.contactListViewModel,
|
||||
) : _cryptoNumberFormat = NumberFormat(),
|
||||
isFixedRateMode = false,
|
||||
isReceiveAmountEntered = false,
|
||||
depositAmount = '',
|
||||
|
@ -70,10 +77,11 @@ abstract class ExchangeViewModelBase with Store {
|
|||
limits = Limits(min: 0, max: 0),
|
||||
tradeState = ExchangeTradeStateInitial(),
|
||||
limitsState = LimitsInitialState(),
|
||||
receiveCurrency = wallet.currency,
|
||||
depositCurrency = wallet.currency,
|
||||
receiveCurrency = appStore.wallet!.currency,
|
||||
depositCurrency = appStore.wallet!.currency,
|
||||
providerList = [],
|
||||
selectedProviders = ObservableList<ExchangeProvider>() {
|
||||
selectedProviders = ObservableList<ExchangeProvider>(),
|
||||
super(appStore: appStore) {
|
||||
_useTorOnly = _settingsStore.exchangeStatus == ExchangeApiMode.torOnly;
|
||||
_setProviders();
|
||||
const excludeDepositCurrencies = [CryptoCurrency.btt, CryptoCurrency.nano];
|
||||
|
@ -86,10 +94,9 @@ abstract class ExchangeViewModelBase with Store {
|
|||
];
|
||||
_initialPairBasedOnWallet();
|
||||
|
||||
final Map<String, dynamic> exchangeProvidersSelection = json.decode(
|
||||
sharedPreferences
|
||||
.getString(PreferencesKey.exchangeProvidersSelection) ??
|
||||
"{}") as Map<String, dynamic>;
|
||||
final Map<String, dynamic> exchangeProvidersSelection =
|
||||
json.decode(sharedPreferences.getString(PreferencesKey.exchangeProvidersSelection) ?? "{}")
|
||||
as Map<String, dynamic>;
|
||||
|
||||
/// if the provider is not in the user settings (user's first time or newly added provider)
|
||||
/// then use its default value decided by us
|
||||
|
@ -102,34 +109,28 @@ abstract class ExchangeViewModelBase with Store {
|
|||
_setAvailableProviders();
|
||||
_calculateBestRate();
|
||||
|
||||
bestRateSync =
|
||||
Timer.periodic(Duration(seconds: 10), (timer) => _calculateBestRate());
|
||||
bestRateSync = Timer.periodic(Duration(seconds: 10), (timer) => _calculateBestRate());
|
||||
|
||||
isDepositAddressEnabled = !(depositCurrency == wallet.currency);
|
||||
isReceiveAddressEnabled = !(receiveCurrency == wallet.currency);
|
||||
depositAmount = '';
|
||||
receiveAmount = '';
|
||||
receiveAddress = '';
|
||||
depositAddress = depositCurrency == wallet.currency
|
||||
? wallet.walletAddresses.address
|
||||
: '';
|
||||
depositAddress = depositCurrency == wallet.currency ? wallet.walletAddresses.address : '';
|
||||
provider = providersForCurrentPair().first;
|
||||
final initialProvider = provider;
|
||||
provider!.checkIsAvailable().then((bool isAvailable) {
|
||||
if (!isAvailable && provider == initialProvider) {
|
||||
provider = providerList.firstWhere(
|
||||
(provider) => provider is ChangeNowExchangeProvider,
|
||||
provider = providerList.firstWhere((provider) => provider is ChangeNowExchangeProvider,
|
||||
orElse: () => providerList.last);
|
||||
_onPairChange();
|
||||
}
|
||||
});
|
||||
receiveCurrencies = CryptoCurrency.all
|
||||
.where((cryptoCurrency) =>
|
||||
!excludeReceiveCurrencies.contains(cryptoCurrency))
|
||||
.where((cryptoCurrency) => !excludeReceiveCurrencies.contains(cryptoCurrency))
|
||||
.toList();
|
||||
depositCurrencies = CryptoCurrency.all
|
||||
.where((cryptoCurrency) =>
|
||||
!excludeDepositCurrencies.contains(cryptoCurrency))
|
||||
.where((cryptoCurrency) => !excludeDepositCurrencies.contains(cryptoCurrency))
|
||||
.toList();
|
||||
_defineIsReceiveAmountEditable();
|
||||
loadLimits();
|
||||
|
@ -140,7 +141,6 @@ abstract class ExchangeViewModelBase with Store {
|
|||
});
|
||||
}
|
||||
bool _useTorOnly;
|
||||
final WalletBase wallet;
|
||||
final Box<Trade> trades;
|
||||
final ExchangeTemplateStore _exchangeTemplateStore;
|
||||
final TradesStore tradesStore;
|
||||
|
@ -165,8 +165,7 @@ abstract class ExchangeViewModelBase with Store {
|
|||
/// initialize with descending comparator
|
||||
/// since we want largest rate first
|
||||
final SplayTreeMap<double, ExchangeProvider> _sortedAvailableProviders =
|
||||
SplayTreeMap<double, ExchangeProvider>(
|
||||
(double a, double b) => b.compareTo(a));
|
||||
SplayTreeMap<double, ExchangeProvider>((double a, double b) => b.compareTo(a));
|
||||
|
||||
final List<ExchangeProvider> _tradeAvailableProviders = [];
|
||||
|
||||
|
@ -222,21 +221,17 @@ abstract class ExchangeViewModelBase with Store {
|
|||
SyncStatus get status => wallet.syncStatus;
|
||||
|
||||
@computed
|
||||
ObservableList<ExchangeTemplate> get templates =>
|
||||
_exchangeTemplateStore.templates;
|
||||
ObservableList<ExchangeTemplate> get templates => _exchangeTemplateStore.templates;
|
||||
|
||||
@computed
|
||||
List<WalletContact> get walletContactsToShow =>
|
||||
contactListViewModel.walletContacts
|
||||
.where((element) =>
|
||||
receiveCurrency == null || element.type == receiveCurrency)
|
||||
.toList();
|
||||
List<WalletContact> get walletContactsToShow => contactListViewModel.walletContacts
|
||||
.where((element) => receiveCurrency == null || element.type == receiveCurrency)
|
||||
.toList();
|
||||
|
||||
@action
|
||||
bool checkIfWalletIsAnInternalWallet(String address) {
|
||||
final walletContactList = walletContactsToShow
|
||||
.where((element) => element.address == address)
|
||||
.toList();
|
||||
final walletContactList =
|
||||
walletContactsToShow.where((element) => element.address == address).toList();
|
||||
|
||||
return walletContactList.isNotEmpty;
|
||||
}
|
||||
|
@ -256,7 +251,6 @@ abstract class ExchangeViewModelBase with Store {
|
|||
return false;
|
||||
}
|
||||
|
||||
|
||||
@computed
|
||||
TransactionPriority get transactionPriority {
|
||||
final priority = _settingsStore.priority[wallet.type];
|
||||
|
@ -269,7 +263,8 @@ abstract class ExchangeViewModelBase with Store {
|
|||
}
|
||||
|
||||
bool get hasAllAmount =>
|
||||
(wallet.type == WalletType.bitcoin || wallet.type == WalletType.litecoin) && depositCurrency == wallet.currency;
|
||||
(wallet.type == WalletType.bitcoin || wallet.type == WalletType.litecoin) &&
|
||||
depositCurrency == wallet.currency;
|
||||
|
||||
bool get isMoneroWallet => wallet.type == WalletType.monero;
|
||||
|
||||
|
@ -277,14 +272,11 @@ abstract class ExchangeViewModelBase with Store {
|
|||
switch (wallet.type) {
|
||||
case WalletType.monero:
|
||||
case WalletType.haven:
|
||||
return transactionPriority ==
|
||||
monero!.getMoneroTransactionPrioritySlow();
|
||||
return transactionPriority == monero!.getMoneroTransactionPrioritySlow();
|
||||
case WalletType.bitcoin:
|
||||
return transactionPriority ==
|
||||
bitcoin!.getBitcoinTransactionPrioritySlow();
|
||||
return transactionPriority == bitcoin!.getBitcoinTransactionPrioritySlow();
|
||||
case WalletType.litecoin:
|
||||
return transactionPriority ==
|
||||
bitcoin!.getLitecoinTransactionPrioritySlow();
|
||||
return transactionPriority == bitcoin!.getLitecoinTransactionPrioritySlow();
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
@ -390,20 +382,18 @@ abstract class ExchangeViewModelBase with Store {
|
|||
}
|
||||
|
||||
Future<void> _calculateBestRate() async {
|
||||
final amount =
|
||||
double.tryParse(isFixedRateMode ? receiveAmount : depositAmount) ?? 1;
|
||||
final amount = double.tryParse(isFixedRateMode ? receiveAmount : depositAmount) ?? 1;
|
||||
|
||||
final _providers = _tradeAvailableProviders
|
||||
.where((element) => !isFixedRateMode || element.supportsFixedRate)
|
||||
.toList();
|
||||
|
||||
final result = await Future.wait<double>(_providers.map((element) =>
|
||||
element.fetchRate(
|
||||
from: depositCurrency,
|
||||
to: receiveCurrency,
|
||||
amount: amount,
|
||||
isFixedRateMode: isFixedRateMode,
|
||||
isReceiveAmount: isFixedRateMode)));
|
||||
final result = await Future.wait<double>(_providers.map((element) => element.fetchRate(
|
||||
from: depositCurrency,
|
||||
to: receiveCurrency,
|
||||
amount: amount,
|
||||
isFixedRateMode: isFixedRateMode,
|
||||
isReceiveAmount: isFixedRateMode)));
|
||||
|
||||
_sortedAvailableProviders.clear();
|
||||
|
||||
|
@ -445,14 +435,13 @@ abstract class ExchangeViewModelBase with Store {
|
|||
}
|
||||
|
||||
try {
|
||||
final tempLimits = await provider.fetchLimits(
|
||||
from: from, to: to, isFixedRateMode: isFixedRateMode);
|
||||
final tempLimits =
|
||||
await provider.fetchLimits(from: from, to: to, isFixedRateMode: isFixedRateMode);
|
||||
|
||||
if (lowestMin != null && (tempLimits.min ?? -1) < lowestMin) {
|
||||
lowestMin = tempLimits.min;
|
||||
}
|
||||
if (highestMax != null &&
|
||||
(tempLimits.max ?? double.maxFinite) > highestMax) {
|
||||
if (highestMax != null && (tempLimits.max ?? double.maxFinite) > highestMax) {
|
||||
highestMax = tempLimits.max;
|
||||
}
|
||||
} catch (e) {
|
||||
|
@ -568,8 +557,8 @@ abstract class ExchangeViewModelBase with Store {
|
|||
} else {
|
||||
try {
|
||||
tradeState = TradeIsCreating();
|
||||
final trade = await provider.createTrade(
|
||||
request: request!, isFixedRateMode: isFixedRateMode);
|
||||
final trade =
|
||||
await provider.createTrade(request: request!, isFixedRateMode: isFixedRateMode);
|
||||
trade.walletId = wallet.id;
|
||||
tradesStore.setTrade(trade);
|
||||
await trades.add(trade);
|
||||
|
@ -604,12 +593,8 @@ abstract class ExchangeViewModelBase with Store {
|
|||
isReceiveAmountEntered = false;
|
||||
depositAmount = '';
|
||||
receiveAmount = '';
|
||||
depositAddress = depositCurrency == wallet.currency
|
||||
? wallet.walletAddresses.address
|
||||
: '';
|
||||
receiveAddress = receiveCurrency == wallet.currency
|
||||
? wallet.walletAddresses.address
|
||||
: '';
|
||||
depositAddress = depositCurrency == wallet.currency ? wallet.walletAddresses.address : '';
|
||||
receiveAddress = receiveCurrency == wallet.currency ? wallet.walletAddresses.address : '';
|
||||
isDepositAddressEnabled = !(depositCurrency == wallet.currency);
|
||||
isReceiveAddressEnabled = !(receiveCurrency == wallet.currency);
|
||||
isFixedRateMode = false;
|
||||
|
@ -628,8 +613,7 @@ abstract class ExchangeViewModelBase with Store {
|
|||
}
|
||||
|
||||
final amount = availableBalance - fee;
|
||||
changeDepositAmount(
|
||||
amount: bitcoin!.formatterBitcoinAmountToString(amount: amount));
|
||||
changeDepositAmount(amount: bitcoin!.formatterBitcoinAmountToString(amount: amount));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -664,9 +648,8 @@ abstract class ExchangeViewModelBase with Store {
|
|||
List<ExchangeProvider> _providersForPair(
|
||||
{required CryptoCurrency from, required CryptoCurrency to}) {
|
||||
final providers = providerList
|
||||
.where((provider) => provider.pairList
|
||||
.where((pair) => pair.from == from && pair.to == to)
|
||||
.isNotEmpty)
|
||||
.where((provider) =>
|
||||
provider.pairList.where((pair) => pair.from == from && pair.to == to).isNotEmpty)
|
||||
.toList();
|
||||
|
||||
return providers;
|
||||
|
@ -746,14 +729,12 @@ abstract class ExchangeViewModelBase with Store {
|
|||
_bestRate = 0;
|
||||
_calculateBestRate();
|
||||
|
||||
final Map<String, dynamic> exchangeProvidersSelection = json.decode(
|
||||
sharedPreferences
|
||||
.getString(PreferencesKey.exchangeProvidersSelection) ??
|
||||
"{}") as Map<String, dynamic>;
|
||||
final Map<String, dynamic> exchangeProvidersSelection =
|
||||
json.decode(sharedPreferences.getString(PreferencesKey.exchangeProvidersSelection) ?? "{}")
|
||||
as Map<String, dynamic>;
|
||||
|
||||
for (var provider in providerList) {
|
||||
exchangeProvidersSelection[provider.title] =
|
||||
selectedProviders.contains(provider);
|
||||
exchangeProvidersSelection[provider.title] = selectedProviders.contains(provider);
|
||||
}
|
||||
|
||||
sharedPreferences.setString(
|
||||
|
@ -764,15 +745,15 @@ abstract class ExchangeViewModelBase with Store {
|
|||
|
||||
bool get isAvailableInSelected {
|
||||
final providersForPair = providersForCurrentPair();
|
||||
return selectedProviders.any(
|
||||
(element) => element.isAvailable && providersForPair.contains(element));
|
||||
return selectedProviders
|
||||
.any((element) => element.isAvailable && providersForPair.contains(element));
|
||||
}
|
||||
|
||||
void _setAvailableProviders() {
|
||||
_tradeAvailableProviders.clear();
|
||||
|
||||
_tradeAvailableProviders.addAll(selectedProviders
|
||||
.where((provider) => providersForCurrentPair().contains(provider)));
|
||||
_tradeAvailableProviders.addAll(
|
||||
selectedProviders.where((provider) => providersForCurrentPair().contains(provider)));
|
||||
}
|
||||
|
||||
@action
|
||||
|
@ -780,16 +761,13 @@ abstract class ExchangeViewModelBase with Store {
|
|||
switch (wallet.type) {
|
||||
case WalletType.monero:
|
||||
case WalletType.haven:
|
||||
_settingsStore.priority[wallet.type] =
|
||||
monero!.getMoneroTransactionPriorityAutomatic();
|
||||
_settingsStore.priority[wallet.type] = monero!.getMoneroTransactionPriorityAutomatic();
|
||||
break;
|
||||
case WalletType.bitcoin:
|
||||
_settingsStore.priority[wallet.type] =
|
||||
bitcoin!.getBitcoinTransactionPriorityMedium();
|
||||
_settingsStore.priority[wallet.type] = bitcoin!.getBitcoinTransactionPriorityMedium();
|
||||
break;
|
||||
case WalletType.litecoin:
|
||||
_settingsStore.priority[wallet.type] =
|
||||
bitcoin!.getLitecoinTransactionPriorityMedium();
|
||||
_settingsStore.priority[wallet.type] = bitcoin!.getLitecoinTransactionPriorityMedium();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
@ -798,9 +776,7 @@ abstract class ExchangeViewModelBase with Store {
|
|||
|
||||
void _setProviders() {
|
||||
if (_settingsStore.exchangeStatus == ExchangeApiMode.torOnly) {
|
||||
providerList = _allProviders
|
||||
.where((provider) => provider.supportsOnionAddress)
|
||||
.toList();
|
||||
providerList = _allProviders.where((provider) => provider.supportsOnionAddress).toList();
|
||||
} else {
|
||||
providerList = _allProviders;
|
||||
}
|
||||
|
|
|
@ -76,6 +76,7 @@ abstract class NodeListViewModelBase with Store {
|
|||
@action
|
||||
Future<void> delete(Node node) async => node.delete();
|
||||
|
||||
@action
|
||||
Future<void> setAsCurrent(Node node) async => settingsStore.nodes[_appStore.wallet!.type] = node;
|
||||
|
||||
@action
|
||||
|
|
|
@ -66,6 +66,9 @@ abstract class WalletRestorationFromQRVMBase extends WalletCreationVM with Store
|
|||
case WalletType.litecoin:
|
||||
return bitcoin!.createBitcoinRestoreWalletFromWIFCredentials(
|
||||
name: name, password: password, wif: wif);
|
||||
case WalletType.ethereum:
|
||||
return ethereum!.createEthereumRestoreWalletFromPrivateKey(
|
||||
name: name, password: password, privateKey: restoreWallet.privateKey!);
|
||||
default:
|
||||
throw Exception('Unexpected type: ${restoreWallet.type.toString()}');
|
||||
}
|
||||
|
|
|
@ -13,7 +13,8 @@ class RestoredWallet {
|
|||
this.txAmount,
|
||||
this.txDescription,
|
||||
this.recipientName,
|
||||
this.height});
|
||||
this.height,
|
||||
this.privateKey});
|
||||
|
||||
final WalletRestoreMode restoreMode;
|
||||
final WalletType type;
|
||||
|
@ -26,6 +27,7 @@ class RestoredWallet {
|
|||
final String? txDescription;
|
||||
final String? recipientName;
|
||||
final int? height;
|
||||
final String? privateKey;
|
||||
|
||||
factory RestoredWallet.fromKey(Map<String, dynamic> json) {
|
||||
final height = json['height'] as String?;
|
||||
|
@ -36,6 +38,7 @@ class RestoredWallet {
|
|||
spendKey: json['spend_key'] as String?,
|
||||
viewKey: json['view_key'] as String?,
|
||||
height: height != null ? int.parse(height) : 0,
|
||||
privateKey: json['private_key'] as String?,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -33,7 +33,10 @@ class WalletRestoreFromQRCode {
|
|||
getSeedPhraseFromUrl(queryParameters.toString(), credentials['type'] as WalletType);
|
||||
if (seed != null) {
|
||||
credentials['seed'] = seed;
|
||||
} else {
|
||||
credentials['private_key'] = queryParameters['private_key'];
|
||||
}
|
||||
|
||||
credentials.addAll(queryParameters);
|
||||
credentials['mode'] = getWalletRestoreMode(credentials);
|
||||
|
||||
|
@ -69,6 +72,8 @@ class WalletRestoreFromQRCode {
|
|||
case 'litecoin':
|
||||
case 'litecoin-wallet':
|
||||
return WalletType.litecoin;
|
||||
case 'ethereum-wallet':
|
||||
return WalletType.ethereum;
|
||||
default:
|
||||
throw Exception('Unexpected wallet type: ${scheme.toString()}');
|
||||
}
|
||||
|
@ -101,6 +106,7 @@ class WalletRestoreFromQRCode {
|
|||
}
|
||||
case WalletType.bitcoin:
|
||||
case WalletType.litecoin:
|
||||
case WalletType.ethereum:
|
||||
RegExp regex24 = RegExp(r'\b(\S+\b\s+){23}\S+\b');
|
||||
RegExp regex18 = RegExp(r'\b(\S+\b\s+){17}\S+\b');
|
||||
RegExp regex12 = RegExp(r'\b(\S+\b\s+){11}\S+\b');
|
||||
|
@ -152,6 +158,14 @@ class WalletRestoreFromQRCode {
|
|||
: throw Exception('Unexpected restore mode: spend_key or view_key is invalid');
|
||||
}
|
||||
|
||||
if (type == WalletType.ethereum && credentials.containsKey('private_key')) {
|
||||
final privateKey = credentials['private_key'] as String;
|
||||
if (privateKey.isEmpty) {
|
||||
throw Exception('Unexpected restore mode: private_key');
|
||||
}
|
||||
return WalletRestoreMode.keys;
|
||||
}
|
||||
|
||||
throw Exception('Unexpected restore mode: restore params are invalid');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import 'package:cake_wallet/core/wallet_change_listener_view_model.dart';
|
||||
import 'package:cake_wallet/entities/contact_record.dart';
|
||||
import 'package:cake_wallet/entities/priority_for_wallet_type.dart';
|
||||
import 'package:cake_wallet/entities/transaction_description.dart';
|
||||
|
@ -15,7 +16,7 @@ import 'package:cake_wallet/core/address_validator.dart';
|
|||
import 'package:cake_wallet/core/amount_validator.dart';
|
||||
import 'package:cw_core/pending_transaction.dart';
|
||||
import 'package:cake_wallet/core/validator.dart';
|
||||
import 'package:cw_core/wallet_base.dart';
|
||||
import 'package:cake_wallet/store/app_store.dart';
|
||||
import 'package:cake_wallet/core/execution_state.dart';
|
||||
import 'package:cake_wallet/monero/monero.dart';
|
||||
import 'package:cw_core/sync_status.dart';
|
||||
|
@ -34,30 +35,38 @@ part 'send_view_model.g.dart';
|
|||
|
||||
class SendViewModel = SendViewModelBase with _$SendViewModel;
|
||||
|
||||
abstract class SendViewModelBase with Store {
|
||||
SendViewModelBase(
|
||||
this._wallet,
|
||||
this._settingsStore,
|
||||
this.sendTemplateViewModel,
|
||||
this._fiatConversationStore,
|
||||
this.balanceViewModel,
|
||||
this.contactListViewModel,
|
||||
this.transactionDescriptionBox)
|
||||
: state = InitialExecutionState(),
|
||||
currencies = _wallet.balance.keys.toList(),
|
||||
selectedCryptoCurrency = _wallet.currency,
|
||||
hasMultipleTokens = _wallet.type == WalletType.ethereum,
|
||||
outputs = ObservableList<Output>(),
|
||||
fiatFromSettings = _settingsStore.fiatCurrency {
|
||||
final priority = _settingsStore.priority[_wallet.type];
|
||||
final priorities = priorityForWalletType(_wallet.type);
|
||||
abstract class SendViewModelBase extends WalletChangeListenerViewModel with Store {
|
||||
@override
|
||||
void onWalletChange(wallet) {
|
||||
currencies = wallet.balance.keys.toList();
|
||||
selectedCryptoCurrency = wallet.currency;
|
||||
hasMultipleTokens = wallet.type == WalletType.ethereum;
|
||||
}
|
||||
|
||||
if (!priorityForWalletType(_wallet.type).contains(priority)) {
|
||||
_settingsStore.priority[_wallet.type] = priorities.first;
|
||||
SendViewModelBase(
|
||||
AppStore appStore,
|
||||
this.sendTemplateViewModel,
|
||||
this._fiatConversationStore,
|
||||
this.balanceViewModel,
|
||||
this.contactListViewModel,
|
||||
this.transactionDescriptionBox,
|
||||
) : state = InitialExecutionState(),
|
||||
currencies = appStore.wallet!.balance.keys.toList(),
|
||||
selectedCryptoCurrency = appStore.wallet!.currency,
|
||||
hasMultipleTokens = appStore.wallet!.type == WalletType.ethereum,
|
||||
outputs = ObservableList<Output>(),
|
||||
_settingsStore = appStore.settingsStore,
|
||||
fiatFromSettings = appStore.settingsStore.fiatCurrency,
|
||||
super(appStore: appStore) {
|
||||
final priority = _settingsStore.priority[wallet.type];
|
||||
final priorities = priorityForWalletType(wallet.type);
|
||||
|
||||
if (!priorityForWalletType(wallet.type).contains(priority)) {
|
||||
_settingsStore.priority[wallet.type] = priorities.first;
|
||||
}
|
||||
|
||||
outputs
|
||||
.add(Output(_wallet, _settingsStore, _fiatConversationStore, () => selectedCryptoCurrency));
|
||||
.add(Output(wallet, _settingsStore, _fiatConversationStore, () => selectedCryptoCurrency));
|
||||
}
|
||||
|
||||
@observable
|
||||
|
@ -68,7 +77,7 @@ abstract class SendViewModelBase with Store {
|
|||
@action
|
||||
void addOutput() {
|
||||
outputs
|
||||
.add(Output(_wallet, _settingsStore, _fiatConversationStore, () => selectedCryptoCurrency));
|
||||
.add(Output(wallet, _settingsStore, _fiatConversationStore, () => selectedCryptoCurrency));
|
||||
}
|
||||
|
||||
@action
|
||||
|
@ -107,9 +116,8 @@ abstract class SendViewModelBase with Store {
|
|||
String get pendingTransactionFeeFiatAmount {
|
||||
try {
|
||||
if (pendingTransaction != null) {
|
||||
final currency = walletType == WalletType.ethereum
|
||||
? _wallet.currency
|
||||
: selectedCryptoCurrency;
|
||||
final currency =
|
||||
walletType == WalletType.ethereum ? wallet.currency : selectedCryptoCurrency;
|
||||
final fiat = calculateFiatAmount(
|
||||
price: _fiatConversationStore.prices[currency]!,
|
||||
cryptoAmount: pendingTransaction!.feeFormatted);
|
||||
|
@ -125,19 +133,19 @@ abstract class SendViewModelBase with Store {
|
|||
FiatCurrency get fiat => _settingsStore.fiatCurrency;
|
||||
|
||||
TransactionPriority get transactionPriority {
|
||||
final priority = _settingsStore.priority[_wallet.type];
|
||||
final priority = _settingsStore.priority[wallet.type];
|
||||
|
||||
if (priority == null) {
|
||||
throw Exception('Unexpected type ${_wallet.type}');
|
||||
throw Exception('Unexpected type ${wallet.type}');
|
||||
}
|
||||
|
||||
return priority;
|
||||
}
|
||||
|
||||
CryptoCurrency get currency => _wallet.currency;
|
||||
CryptoCurrency get currency => wallet.currency;
|
||||
|
||||
Validator<String> get amountValidator =>
|
||||
AmountValidator(currency: walletTypeToCryptoCurrency(_wallet.type));
|
||||
AmountValidator(currency: walletTypeToCryptoCurrency(wallet.type));
|
||||
|
||||
Validator<String> get allAmountValidator => AllAmountValidator();
|
||||
|
||||
|
@ -151,7 +159,7 @@ abstract class SendViewModelBase with Store {
|
|||
PendingTransaction? pendingTransaction;
|
||||
|
||||
@computed
|
||||
String get balance => _wallet.balance[selectedCryptoCurrency]!.formattedAvailableBalance;
|
||||
String get balance => wallet.balance[selectedCryptoCurrency]!.formattedAvailableBalance;
|
||||
|
||||
@computed
|
||||
bool get isFiatDisabled => balanceViewModel.isFiatDisabled;
|
||||
|
@ -165,53 +173,58 @@ abstract class SendViewModelBase with Store {
|
|||
isFiatDisabled ? '' : pendingTransactionFeeFiatAmount + ' ' + fiat.title;
|
||||
|
||||
@computed
|
||||
bool get isReadyForSend => _wallet.syncStatus is SyncedSyncStatus;
|
||||
bool get isReadyForSend => wallet.syncStatus is SyncedSyncStatus;
|
||||
|
||||
@computed
|
||||
List<Template> get templates => sendTemplateViewModel.templates
|
||||
.where((template) => _isEqualCurrency(template.cryptoCurrency))
|
||||
.toList();
|
||||
|
||||
@computed
|
||||
bool get hasCoinControl =>
|
||||
wallet.type == WalletType.bitcoin ||
|
||||
wallet.type == WalletType.litecoin ||
|
||||
wallet.type == WalletType.monero;
|
||||
|
||||
@computed
|
||||
bool get isElectrumWallet =>
|
||||
_wallet.type == WalletType.bitcoin || _wallet.type == WalletType.litecoin;
|
||||
wallet.type == WalletType.bitcoin || wallet.type == WalletType.litecoin;
|
||||
|
||||
@observable
|
||||
CryptoCurrency selectedCryptoCurrency;
|
||||
|
||||
List<CryptoCurrency> currencies;
|
||||
|
||||
bool get hasYat => outputs.any((out) =>
|
||||
out.isParsedAddress &&
|
||||
out.parsedAddress.parseFrom == ParseFrom.yatRecord);
|
||||
bool get hasYat => outputs
|
||||
.any((out) => out.isParsedAddress && out.parsedAddress.parseFrom == ParseFrom.yatRecord);
|
||||
|
||||
WalletType get walletType => _wallet.type;
|
||||
WalletType get walletType => wallet.type;
|
||||
|
||||
String? get walletCurrencyName =>
|
||||
_wallet.currency.fullName?.toLowerCase() ?? _wallet.currency.name;
|
||||
String? get walletCurrencyName => wallet.currency.fullName?.toLowerCase() ?? wallet.currency.name;
|
||||
|
||||
bool get hasCurrecyChanger => walletType == WalletType.haven;
|
||||
|
||||
@computed
|
||||
FiatCurrency get fiatCurrency => _settingsStore.fiatCurrency;
|
||||
|
||||
final WalletBase _wallet;
|
||||
final SettingsStore _settingsStore;
|
||||
final SendTemplateViewModel sendTemplateViewModel;
|
||||
final BalanceViewModel balanceViewModel;
|
||||
final ContactListViewModel contactListViewModel;
|
||||
final FiatConversionStore _fiatConversationStore;
|
||||
final Box<TransactionDescription> transactionDescriptionBox;
|
||||
final bool hasMultipleTokens;
|
||||
|
||||
@observable
|
||||
bool hasMultipleTokens;
|
||||
|
||||
@computed
|
||||
List<ContactRecord> get contactsToShow => contactListViewModel.contacts
|
||||
.where((element) => selectedCryptoCurrency == null || element.type == selectedCryptoCurrency)
|
||||
.where((element) => element.type == selectedCryptoCurrency)
|
||||
.toList();
|
||||
|
||||
@computed
|
||||
List<WalletContact> get walletContactsToShow => contactListViewModel.walletContacts
|
||||
.where((element) => selectedCryptoCurrency == null || element.type == selectedCryptoCurrency)
|
||||
.where((element) => element.type == selectedCryptoCurrency)
|
||||
.toList();
|
||||
|
||||
@action
|
||||
|
@ -271,7 +284,7 @@ abstract class SendViewModelBase with Store {
|
|||
Future<void> createTransaction() async {
|
||||
try {
|
||||
state = IsExecutingState();
|
||||
pendingTransaction = await _wallet.createTransaction(_credentials());
|
||||
pendingTransaction = await wallet.createTransaction(_credentials());
|
||||
state = ExecutedSuccessfullyState();
|
||||
} catch (e) {
|
||||
state = FailureState(e.toString());
|
||||
|
@ -318,61 +331,60 @@ abstract class SendViewModelBase with Store {
|
|||
|
||||
@action
|
||||
void setTransactionPriority(TransactionPriority priority) =>
|
||||
_settingsStore.priority[_wallet.type] = priority;
|
||||
_settingsStore.priority[wallet.type] = priority;
|
||||
|
||||
Object _credentials() {
|
||||
switch (_wallet.type) {
|
||||
switch (wallet.type) {
|
||||
case WalletType.bitcoin:
|
||||
final priority = _settingsStore.priority[_wallet.type];
|
||||
final priority = _settingsStore.priority[wallet.type];
|
||||
|
||||
if (priority == null) {
|
||||
throw Exception('Priority is null for wallet type: ${_wallet.type}');
|
||||
throw Exception('Priority is null for wallet type: ${wallet.type}');
|
||||
}
|
||||
|
||||
return bitcoin!.createBitcoinTransactionCredentials(outputs, priority: priority);
|
||||
case WalletType.litecoin:
|
||||
final priority = _settingsStore.priority[_wallet.type];
|
||||
final priority = _settingsStore.priority[wallet.type];
|
||||
|
||||
if (priority == null) {
|
||||
throw Exception('Priority is null for wallet type: ${_wallet.type}');
|
||||
throw Exception('Priority is null for wallet type: ${wallet.type}');
|
||||
}
|
||||
|
||||
return bitcoin!.createBitcoinTransactionCredentials(outputs, priority: priority);
|
||||
case WalletType.monero:
|
||||
final priority = _settingsStore.priority[_wallet.type];
|
||||
final priority = _settingsStore.priority[wallet.type];
|
||||
|
||||
if (priority == null) {
|
||||
throw Exception('Priority is null for wallet type: ${_wallet.type}');
|
||||
throw Exception('Priority is null for wallet type: ${wallet.type}');
|
||||
}
|
||||
|
||||
return monero!
|
||||
.createMoneroTransactionCreationCredentials(outputs: outputs, priority: priority);
|
||||
case WalletType.haven:
|
||||
final priority = _settingsStore.priority[_wallet.type];
|
||||
final priority = _settingsStore.priority[wallet.type];
|
||||
|
||||
if (priority == null) {
|
||||
throw Exception('Priority is null for wallet type: ${_wallet.type}');
|
||||
throw Exception('Priority is null for wallet type: ${wallet.type}');
|
||||
}
|
||||
|
||||
return haven!.createHavenTransactionCreationCredentials(
|
||||
outputs: outputs, priority: priority, assetType: selectedCryptoCurrency.title);
|
||||
case WalletType.ethereum:
|
||||
final priority = _settingsStore.priority[_wallet.type];
|
||||
final priority = _settingsStore.priority[wallet.type];
|
||||
|
||||
if (priority == null) {
|
||||
throw Exception('Priority is null for wallet type: ${_wallet.type}');
|
||||
throw Exception('Priority is null for wallet type: ${wallet.type}');
|
||||
}
|
||||
|
||||
return ethereum!.createEthereumTransactionCredentials(
|
||||
outputs, priority: priority, currency: selectedCryptoCurrency);
|
||||
return ethereum!.createEthereumTransactionCredentials(outputs,
|
||||
priority: priority, currency: selectedCryptoCurrency);
|
||||
default:
|
||||
throw Exception('Unexpected wallet type: ${_wallet.type}');
|
||||
throw Exception('Unexpected wallet type: ${wallet.type}');
|
||||
}
|
||||
}
|
||||
|
||||
String displayFeeRate(dynamic priority) {
|
||||
final _priority = priority as TransactionPriority;
|
||||
final wallet = _wallet;
|
||||
|
||||
if (isElectrumWallet) {
|
||||
final rate = bitcoin!.getFeeRate(wallet, _priority);
|
||||
|
@ -383,22 +395,21 @@ abstract class SendViewModelBase with Store {
|
|||
}
|
||||
|
||||
bool _isEqualCurrency(String currency) =>
|
||||
_wallet.balance.keys.any((e) => currency.toLowerCase() == e.title.toLowerCase());
|
||||
wallet.balance.keys.any((e) => currency.toLowerCase() == e.title.toLowerCase());
|
||||
|
||||
@action
|
||||
void onClose() => _settingsStore.fiatCurrency = fiatFromSettings;
|
||||
|
||||
@action
|
||||
void setFiatCurrency(FiatCurrency fiat) =>
|
||||
_settingsStore.fiatCurrency = fiat;
|
||||
void setFiatCurrency(FiatCurrency fiat) => _settingsStore.fiatCurrency = fiat;
|
||||
|
||||
@action
|
||||
void setSelectedCryptoCurrency(String cryptoCurrency) {
|
||||
try {
|
||||
selectedCryptoCurrency = _wallet.balance.keys
|
||||
selectedCryptoCurrency = wallet.balance.keys
|
||||
.firstWhere((e) => cryptoCurrency.toLowerCase() == e.title.toLowerCase());
|
||||
} catch (e) {
|
||||
selectedCryptoCurrency = _wallet.currency;
|
||||
selectedCryptoCurrency = wallet.currency;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -96,9 +96,7 @@ abstract class Setup2FAViewModelBase with Store {
|
|||
|
||||
@action
|
||||
void _setBase32SecretKey(String value) {
|
||||
if (_settingsStore.totpSecretKey == '') {
|
||||
_settingsStore.totpSecretKey = value;
|
||||
}
|
||||
_settingsStore.totpSecretKey = value;
|
||||
}
|
||||
|
||||
@action
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
import 'package:cake_wallet/entities/auto_generate_subaddress_status.dart';
|
||||
import 'package:cake_wallet/entities/exchange_api_mode.dart';
|
||||
import 'package:cake_wallet/ethereum/ethereum.dart';
|
||||
import 'package:cake_wallet/store/settings_store.dart';
|
||||
import 'package:cw_core/balance.dart';
|
||||
import 'package:cw_core/transaction_history.dart';
|
||||
import 'package:cw_core/transaction_info.dart';
|
||||
import 'package:cw_core/wallet_base.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
|
@ -14,11 +18,27 @@ abstract class PrivacySettingsViewModelBase with Store {
|
|||
PrivacySettingsViewModelBase(this._settingsStore, this._wallet);
|
||||
|
||||
final SettingsStore _settingsStore;
|
||||
final WalletBase _wallet;
|
||||
final WalletBase<Balance, TransactionHistoryBase<TransactionInfo>, TransactionInfo> _wallet;
|
||||
|
||||
@computed
|
||||
ExchangeApiMode get exchangeStatus => _settingsStore.exchangeStatus;
|
||||
|
||||
@computed
|
||||
bool get isAutoGenerateSubaddressesEnabled =>
|
||||
_settingsStore.autoGenerateSubaddressStatus != AutoGenerateSubaddressStatus.disabled;
|
||||
|
||||
@action
|
||||
void setAutoGenerateSubaddresses(bool value) {
|
||||
_wallet.isEnabledAutoGenerateSubaddress = value;
|
||||
if (value) {
|
||||
_settingsStore.autoGenerateSubaddressStatus = AutoGenerateSubaddressStatus.enabled;
|
||||
} else {
|
||||
_settingsStore.autoGenerateSubaddressStatus = AutoGenerateSubaddressStatus.disabled;
|
||||
}
|
||||
}
|
||||
|
||||
bool get isAutoGenerateSubaddressesVisible => _wallet.type == WalletType.monero;
|
||||
|
||||
@computed
|
||||
bool get shouldSaveRecipientAddress => _settingsStore.shouldSaveRecipientAddress;
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@ abstract class UnspentCoinsDetailsViewModelBase with Store {
|
|||
note = unspentCoinsItem.note {
|
||||
items = [
|
||||
StandartListItem(title: S.current.transaction_details_amount, value: unspentCoinsItem.amount),
|
||||
StandartListItem(title: S.current.transaction_details_transaction_id, value: unspentCoinsItem.hash),
|
||||
StandartListItem(title: S.current.widgets_address, value: unspentCoinsItem.address),
|
||||
TextFieldListItem(
|
||||
title: S.current.note_tap_to_change,
|
||||
|
@ -42,19 +43,22 @@ abstract class UnspentCoinsDetailsViewModelBase with Store {
|
|||
unspentCoinsItem.isSending = !value;
|
||||
}
|
||||
await unspentCoinsListViewModel.saveUnspentCoinInfo(unspentCoinsItem);
|
||||
}),
|
||||
BlockExplorerListItem(
|
||||
title: S.current.view_in_block_explorer,
|
||||
value: _explorerDescription(unspentCoinsListViewModel.wallet.type),
|
||||
onTap: () {
|
||||
try {
|
||||
final url = Uri.parse(
|
||||
_explorerUrl(unspentCoinsListViewModel.wallet.type, unspentCoinsItem.hash));
|
||||
return launchUrl(url);
|
||||
} catch (e) {}
|
||||
|
||||
})
|
||||
];
|
||||
|
||||
if ([WalletType.bitcoin, WalletType.litecoin].contains(unspentCoinsListViewModel.wallet.type)) {
|
||||
items.add(BlockExplorerListItem(
|
||||
title: S.current.view_in_block_explorer,
|
||||
value: _explorerDescription(unspentCoinsListViewModel.wallet.type),
|
||||
onTap: () {
|
||||
try {
|
||||
final url = Uri.parse(
|
||||
_explorerUrl(unspentCoinsListViewModel.wallet.type, unspentCoinsItem.hash));
|
||||
return launchUrl(url);
|
||||
} catch (e) {}
|
||||
},
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
String _explorerUrl(WalletType type, String txId) {
|
||||
|
|
|
@ -13,7 +13,9 @@ abstract class UnspentCoinsItemBase with Store {
|
|||
required this.note,
|
||||
required this.isSending,
|
||||
required this.amountRaw,
|
||||
required this.vout});
|
||||
required this.vout,
|
||||
required this.keyImage
|
||||
});
|
||||
|
||||
@observable
|
||||
String address;
|
||||
|
@ -38,4 +40,7 @@ abstract class UnspentCoinsItemBase with Store {
|
|||
|
||||
@observable
|
||||
int vout;
|
||||
}
|
||||
|
||||
@observable
|
||||
String? keyImage;
|
||||
}
|
||||
|
|
|
@ -1,10 +1,15 @@
|
|||
import 'package:cw_core/unspent_coins_info.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
||||
import 'package:cw_core/wallet_base.dart';
|
||||
import 'package:cake_wallet/entities/unspent_transaction_output.dart';
|
||||
import 'package:cake_wallet/monero/monero.dart';
|
||||
import 'package:cake_wallet/view_model/unspent_coins/unspent_coins_item.dart';
|
||||
import 'package:cw_bitcoin/bitcoin_wallet.dart';
|
||||
import 'package:cw_core/unspent_coins_info.dart';
|
||||
import 'package:cw_core/wallet_base.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:cw_monero/monero_wallet.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
|
||||
part 'unspent_coins_list_view_model.g.dart';
|
||||
|
||||
|
@ -14,7 +19,7 @@ abstract class UnspentCoinsListViewModelBase with Store {
|
|||
UnspentCoinsListViewModelBase(
|
||||
{required this.wallet, required Box<UnspentCoinsInfo> unspentCoinsInfo})
|
||||
: _unspentCoinsInfo = unspentCoinsInfo {
|
||||
bitcoin!.updateUnspents(wallet);
|
||||
_updateUnspents();
|
||||
}
|
||||
|
||||
WalletBase wallet;
|
||||
|
@ -22,11 +27,10 @@ abstract class UnspentCoinsListViewModelBase with Store {
|
|||
|
||||
@computed
|
||||
ObservableList<UnspentCoinsItem> get items =>
|
||||
ObservableList.of(bitcoin!.getUnspents(wallet).map((elem) {
|
||||
final amount = bitcoin!.formatterBitcoinAmountToString(amount: elem.value) +
|
||||
' ${wallet.currency.title}';
|
||||
ObservableList.of(_getUnspents().map((elem) {
|
||||
final amount = formatAmountToString(elem.value) + ' ${wallet.currency.title}';
|
||||
|
||||
final info = getUnspentCoinInfo(elem.hash, elem.address, elem.value, elem.vout);
|
||||
final info = getUnspentCoinInfo(elem.hash, elem.address, elem.value, elem.vout, elem.keyImage);
|
||||
|
||||
return UnspentCoinsItem(
|
||||
address: elem.address,
|
||||
|
@ -36,12 +40,14 @@ abstract class UnspentCoinsListViewModelBase with Store {
|
|||
note: info?.note ?? '',
|
||||
isSending: info?.isSending ?? true,
|
||||
amountRaw: elem.value,
|
||||
vout: elem.vout);
|
||||
vout: elem.vout,
|
||||
keyImage: elem.keyImage
|
||||
);
|
||||
}));
|
||||
|
||||
Future<void> saveUnspentCoinInfo(UnspentCoinsItem item) async {
|
||||
try {
|
||||
final info = getUnspentCoinInfo(item.hash, item.address, item.amountRaw, item.vout);
|
||||
final info = getUnspentCoinInfo(item.hash, item.address, item.amountRaw, item.vout, item.keyImage);
|
||||
if (info == null) {
|
||||
final newInfo = UnspentCoinsInfo(
|
||||
walletId: wallet.id,
|
||||
|
@ -51,10 +57,12 @@ abstract class UnspentCoinsListViewModelBase with Store {
|
|||
vout: item.vout,
|
||||
isFrozen: item.isFrozen,
|
||||
isSending: item.isSending,
|
||||
noteRaw: item.note);
|
||||
noteRaw: item.note,
|
||||
keyImage: item.keyImage
|
||||
);
|
||||
|
||||
await _unspentCoinsInfo.add(newInfo);
|
||||
bitcoin!.updateUnspents(wallet);
|
||||
_updateUnspents();
|
||||
wallet.updateBalance();
|
||||
return;
|
||||
}
|
||||
|
@ -63,19 +71,45 @@ abstract class UnspentCoinsListViewModelBase with Store {
|
|||
info.note = item.note;
|
||||
|
||||
await info.save();
|
||||
bitcoin!.updateUnspents(wallet);
|
||||
_updateUnspents();
|
||||
wallet.updateBalance();
|
||||
} catch (e) {
|
||||
print(e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
UnspentCoinsInfo? getUnspentCoinInfo(String hash, String address, int value, int vout) {
|
||||
UnspentCoinsInfo? getUnspentCoinInfo(String hash, String address, int value, int vout, String? keyImage) {
|
||||
return _unspentCoinsInfo.values.firstWhereOrNull((element) =>
|
||||
element.walletId == wallet.id &&
|
||||
element.hash == hash &&
|
||||
element.address == address &&
|
||||
element.value == value &&
|
||||
element.vout == vout);
|
||||
element.vout == vout &&
|
||||
element.keyImage == keyImage
|
||||
);
|
||||
}
|
||||
|
||||
String formatAmountToString(int fullBalance) {
|
||||
if (wallet.type == WalletType.monero)
|
||||
return monero!.formatterMoneroAmountToString(amount: fullBalance);
|
||||
if ([WalletType.bitcoin, WalletType.litecoin].contains(wallet.type))
|
||||
return bitcoin!.formatterBitcoinAmountToString(amount: fullBalance);
|
||||
return '';
|
||||
}
|
||||
|
||||
|
||||
void _updateUnspents() {
|
||||
if (wallet.type == WalletType.monero)
|
||||
return monero!.updateUnspents(wallet);
|
||||
if ([WalletType.bitcoin, WalletType.litecoin].contains(wallet.type))
|
||||
return bitcoin!.updateUnspents(wallet);
|
||||
}
|
||||
|
||||
List<Unspent> _getUnspents() {
|
||||
if (wallet.type == WalletType.monero)
|
||||
return monero!.getUnspents(wallet);
|
||||
if ([WalletType.bitcoin, WalletType.litecoin].contains(wallet.type))
|
||||
return bitcoin!.getUnspents(wallet);
|
||||
return List.empty();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import 'package:cake_wallet/core/wallet_change_listener_view_model.dart';
|
||||
import 'package:cake_wallet/ethereum/ethereum.dart';
|
||||
import 'package:cake_wallet/entities/fiat_currency.dart';
|
||||
import 'package:cake_wallet/store/dashboard/fiat_conversion_store.dart';
|
||||
|
@ -5,16 +6,12 @@ import 'package:cake_wallet/store/yat/yat_store.dart';
|
|||
import 'package:cw_core/currency.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:cw_core/wallet_base.dart';
|
||||
import 'package:cake_wallet/utils/list_item.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_address_list/wallet_account_list_header.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_header.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_item.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
||||
import 'package:cw_core/transaction_history.dart';
|
||||
import 'package:cw_core/balance.dart';
|
||||
import 'package:cw_core/transaction_info.dart';
|
||||
import 'package:cake_wallet/store/app_store.dart';
|
||||
import 'package:cake_wallet/monero/monero.dart';
|
||||
import 'package:cake_wallet/haven/haven.dart';
|
||||
|
@ -110,29 +107,36 @@ class EthereumURI extends PaymentURI {
|
|||
}
|
||||
}
|
||||
|
||||
abstract class WalletAddressListViewModelBase with Store {
|
||||
abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewModel with Store {
|
||||
WalletAddressListViewModelBase({
|
||||
required AppStore appStore,
|
||||
required this.yatStore,
|
||||
required this.fiatConversionStore,
|
||||
}) : _appStore = appStore,
|
||||
_baseItems = <ListItem>[],
|
||||
_wallet = appStore.wallet!,
|
||||
}) : _baseItems = <ListItem>[],
|
||||
selectedCurrency = walletTypeToCryptoCurrency(appStore.wallet!.type),
|
||||
_cryptoNumberFormat = NumberFormat(_cryptoNumberPattern),
|
||||
hasAccounts =
|
||||
appStore.wallet!.type == WalletType.monero || appStore.wallet!.type == WalletType.haven,
|
||||
amount = '' {
|
||||
amount = '',
|
||||
super(appStore: appStore) {
|
||||
_init();
|
||||
}
|
||||
|
||||
@override
|
||||
void onWalletChange(wallet) {
|
||||
_init();
|
||||
|
||||
selectedCurrency = walletTypeToCryptoCurrency(wallet.type);
|
||||
hasAccounts = wallet.type == WalletType.monero || wallet.type == WalletType.haven;
|
||||
}
|
||||
|
||||
static const String _cryptoNumberPattern = '0.00000000';
|
||||
|
||||
final NumberFormat _cryptoNumberFormat;
|
||||
|
||||
final FiatConversionStore fiatConversionStore;
|
||||
|
||||
List<Currency> get currencies => [walletTypeToCryptoCurrency(_wallet.type), ...FiatCurrency.all];
|
||||
List<Currency> get currencies => [walletTypeToCryptoCurrency(wallet.type), ...FiatCurrency.all];
|
||||
|
||||
@observable
|
||||
Currency selectedCurrency;
|
||||
|
@ -144,31 +148,31 @@ abstract class WalletAddressListViewModelBase with Store {
|
|||
String amount;
|
||||
|
||||
@computed
|
||||
WalletType get type => _wallet.type;
|
||||
WalletType get type => wallet.type;
|
||||
|
||||
@computed
|
||||
WalletAddressListItem get address =>
|
||||
WalletAddressListItem(address: _wallet.walletAddresses.address, isPrimary: false);
|
||||
WalletAddressListItem(address: wallet.walletAddresses.address, isPrimary: false);
|
||||
|
||||
@computed
|
||||
PaymentURI get uri {
|
||||
if (_wallet.type == WalletType.monero) {
|
||||
if (wallet.type == WalletType.monero) {
|
||||
return MoneroURI(amount: amount, address: address.address);
|
||||
}
|
||||
|
||||
if (_wallet.type == WalletType.haven) {
|
||||
if (wallet.type == WalletType.haven) {
|
||||
return HavenURI(amount: amount, address: address.address);
|
||||
}
|
||||
|
||||
if (_wallet.type == WalletType.bitcoin) {
|
||||
if (wallet.type == WalletType.bitcoin) {
|
||||
return BitcoinURI(amount: amount, address: address.address);
|
||||
}
|
||||
|
||||
if (_wallet.type == WalletType.litecoin) {
|
||||
if (wallet.type == WalletType.litecoin) {
|
||||
return LitecoinURI(amount: amount, address: address.address);
|
||||
}
|
||||
|
||||
if (_wallet.type == WalletType.ethereum) {
|
||||
if (wallet.type == WalletType.ethereum) {
|
||||
return EthereumURI(amount: amount, address: address.address);
|
||||
}
|
||||
|
||||
|
@ -182,7 +186,6 @@ abstract class WalletAddressListViewModelBase with Store {
|
|||
|
||||
@computed
|
||||
ObservableList<ListItem> get addressList {
|
||||
final wallet = _wallet;
|
||||
final addressList = ObservableList<ListItem>();
|
||||
|
||||
if (wallet.type == WalletType.monero) {
|
||||
|
@ -237,8 +240,6 @@ abstract class WalletAddressListViewModelBase with Store {
|
|||
|
||||
@computed
|
||||
String get accountLabel {
|
||||
final wallet = _wallet;
|
||||
|
||||
if (wallet.type == WalletType.monero) {
|
||||
return monero!.getCurrentAccount(wallet).label;
|
||||
}
|
||||
|
@ -251,29 +252,24 @@ abstract class WalletAddressListViewModelBase with Store {
|
|||
}
|
||||
|
||||
@computed
|
||||
bool get hasAddressList => _wallet.type == WalletType.monero || _wallet.type == WalletType.haven;
|
||||
bool get hasAddressList => wallet.type == WalletType.monero || wallet.type == WalletType.haven;
|
||||
|
||||
@computed
|
||||
bool get showElectrumAddressDisclaimer =>
|
||||
_wallet.type == WalletType.bitcoin || _wallet.type == WalletType.litecoin;
|
||||
|
||||
@observable
|
||||
WalletBase<Balance, TransactionHistoryBase<TransactionInfo>, TransactionInfo> _wallet;
|
||||
wallet.type == WalletType.bitcoin || wallet.type == WalletType.litecoin;
|
||||
|
||||
List<ListItem> _baseItems;
|
||||
|
||||
AppStore _appStore;
|
||||
|
||||
final YatStore yatStore;
|
||||
|
||||
@action
|
||||
void setAddress(WalletAddressListItem address) =>
|
||||
_wallet.walletAddresses.address = address.address;
|
||||
wallet.walletAddresses.address = address.address;
|
||||
|
||||
void _init() {
|
||||
_baseItems = [];
|
||||
|
||||
if (_wallet.type == WalletType.monero || _wallet.type == WalletType.haven) {
|
||||
if (wallet.type == WalletType.monero || wallet.type == WalletType.haven) {
|
||||
_baseItems.add(WalletAccountListHeader());
|
||||
}
|
||||
|
||||
|
@ -294,7 +290,7 @@ abstract class WalletAddressListViewModelBase with Store {
|
|||
}
|
||||
|
||||
void _convertAmountToCrypto() {
|
||||
final cryptoCurrency = walletTypeToCryptoCurrency(_wallet.type);
|
||||
final cryptoCurrency = walletTypeToCryptoCurrency(wallet.type);
|
||||
try {
|
||||
final crypto =
|
||||
double.parse(amount.replaceAll(',', '.')) / fiatConversionStore.prices[cryptoCurrency]!;
|
||||
|
|
|
@ -69,7 +69,7 @@ abstract class WalletKeysViewModelBase with Store {
|
|||
StandartListItem(title: S.current.view_key_public, value: keys['publicViewKey']!),
|
||||
if (keys['privateViewKey'] != null)
|
||||
StandartListItem(title: S.current.view_key_private, value: keys['privateViewKey']!),
|
||||
StandartListItem(title: S.current.wallet_seed, value: _appStore.wallet!.seed),
|
||||
StandartListItem(title: S.current.wallet_seed, value: _appStore.wallet!.seed!),
|
||||
]);
|
||||
}
|
||||
|
||||
|
@ -85,15 +85,23 @@ abstract class WalletKeysViewModelBase with Store {
|
|||
StandartListItem(title: S.current.view_key_public, value: keys['publicViewKey']!),
|
||||
if (keys['privateViewKey'] != null)
|
||||
StandartListItem(title: S.current.view_key_private, value: keys['privateViewKey']!),
|
||||
StandartListItem(title: S.current.wallet_seed, value: _appStore.wallet!.seed),
|
||||
StandartListItem(title: S.current.wallet_seed, value: _appStore.wallet!.seed!),
|
||||
]);
|
||||
}
|
||||
|
||||
if (_appStore.wallet!.type == WalletType.bitcoin ||
|
||||
_appStore.wallet!.type == WalletType.litecoin ||
|
||||
_appStore.wallet!.type == WalletType.ethereum) {
|
||||
_appStore.wallet!.type == WalletType.litecoin) {
|
||||
items.addAll([
|
||||
StandartListItem(title: S.current.wallet_seed, value: _appStore.wallet!.seed),
|
||||
StandartListItem(title: S.current.wallet_seed, value: _appStore.wallet!.seed!),
|
||||
]);
|
||||
}
|
||||
|
||||
if (_appStore.wallet!.type == WalletType.ethereum) {
|
||||
items.addAll([
|
||||
if (_appStore.wallet!.privateKey != null)
|
||||
StandartListItem(title: S.current.private_key, value: _appStore.wallet!.privateKey!),
|
||||
if (_appStore.wallet!.seed != null)
|
||||
StandartListItem(title: S.current.wallet_seed, value: _appStore.wallet!.seed!),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
@ -139,7 +147,8 @@ abstract class WalletKeysViewModelBase with Store {
|
|||
Future<Map<String, String>> get _queryParams async {
|
||||
final restoreHeightResult = await restoreHeight;
|
||||
return {
|
||||
'seed': _appStore.wallet!.seed,
|
||||
if (_appStore.wallet!.seed != null) 'seed': _appStore.wallet!.seed!,
|
||||
if (_appStore.wallet!.privateKey != null) 'private_key': _appStore.wallet!.privateKey!,
|
||||
if (restoreHeightResult != null) ...{'height': restoreHeightResult}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,77 +0,0 @@
|
|||
import 'package:cake_wallet/view_model/restore/restore_wallet.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:cake_wallet/monero/monero.dart';
|
||||
import 'package:cake_wallet/store/app_store.dart';
|
||||
import 'package:cw_core/wallet_base.dart';
|
||||
import 'package:cake_wallet/core/generate_wallet_password.dart';
|
||||
import 'package:cake_wallet/core/wallet_creation_service.dart';
|
||||
import 'package:cw_core/wallet_credentials.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_creation_vm.dart';
|
||||
import 'package:cw_core/wallet_info.dart';
|
||||
import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
||||
|
||||
part 'wallet_restoration_from_keys_vm.g.dart';
|
||||
|
||||
class WalletRestorationFromKeysVM = WalletRestorationFromKeysVMBase
|
||||
with _$WalletRestorationFromKeysVM;
|
||||
|
||||
abstract class WalletRestorationFromKeysVMBase extends WalletCreationVM
|
||||
with Store {
|
||||
WalletRestorationFromKeysVMBase(AppStore appStore,
|
||||
WalletCreationService walletCreationService, Box<WalletInfo> walletInfoSource,
|
||||
{required WalletType type, required this.language})
|
||||
: height = 0,
|
||||
viewKey = '',
|
||||
spendKey = '',
|
||||
wif = '',
|
||||
address = '',
|
||||
super(appStore, walletInfoSource, walletCreationService, type: type, isRecovery: true);
|
||||
|
||||
@observable
|
||||
int height;
|
||||
|
||||
@observable
|
||||
String viewKey;
|
||||
|
||||
@observable
|
||||
String spendKey;
|
||||
|
||||
@observable
|
||||
String wif;
|
||||
|
||||
@observable
|
||||
String address;
|
||||
|
||||
bool get hasRestorationHeight => type == WalletType.monero;
|
||||
|
||||
final String language;
|
||||
|
||||
@override
|
||||
WalletCredentials getCredentials(dynamic options) {
|
||||
final password = generateWalletPassword();
|
||||
|
||||
switch (type) {
|
||||
case WalletType.monero:
|
||||
return monero!.createMoneroRestoreWalletFromKeysCredentials(
|
||||
name: name,
|
||||
password: password,
|
||||
language: language,
|
||||
address: address,
|
||||
viewKey: viewKey,
|
||||
spendKey: spendKey,
|
||||
height: height);
|
||||
case WalletType.bitcoin:
|
||||
return bitcoin!.createBitcoinRestoreWalletFromWIFCredentials(
|
||||
name: name, password: password, wif: wif);
|
||||
default:
|
||||
throw Exception('Unexpected type: ${type.toString()}');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<WalletBase> process(WalletCredentials credentials) async =>
|
||||
walletCreationService.restoreFromKeys(credentials);
|
||||
}
|
|
@ -1,58 +0,0 @@
|
|||
import 'package:cake_wallet/view_model/restore/restore_wallet.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:cake_wallet/monero/monero.dart';
|
||||
import 'package:cake_wallet/store/app_store.dart';
|
||||
import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
||||
import 'package:cw_core/wallet_base.dart';
|
||||
import 'package:cake_wallet/core/generate_wallet_password.dart';
|
||||
import 'package:cake_wallet/core/wallet_creation_service.dart';
|
||||
import 'package:cw_core/wallet_credentials.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_creation_vm.dart';
|
||||
import 'package:cw_core/wallet_info.dart';
|
||||
|
||||
part 'wallet_restoration_from_seed_vm.g.dart';
|
||||
|
||||
class WalletRestorationFromSeedVM = WalletRestorationFromSeedVMBase
|
||||
with _$WalletRestorationFromSeedVM;
|
||||
|
||||
abstract class WalletRestorationFromSeedVMBase extends WalletCreationVM
|
||||
with Store {
|
||||
WalletRestorationFromSeedVMBase(AppStore appStore,
|
||||
WalletCreationService walletCreationService, Box<WalletInfo> walletInfoSource,
|
||||
{required WalletType type, required this.language, this.seed = ''})
|
||||
: height = 0,
|
||||
super(appStore, walletInfoSource, walletCreationService, type: type, isRecovery: true);
|
||||
|
||||
@observable
|
||||
String seed;
|
||||
|
||||
@observable
|
||||
int height;
|
||||
|
||||
bool get hasRestorationHeight => type == WalletType.monero;
|
||||
|
||||
final String language;
|
||||
|
||||
@override
|
||||
WalletCredentials getCredentials(dynamic options) {
|
||||
final password = generateWalletPassword();
|
||||
|
||||
switch (type) {
|
||||
case WalletType.monero:
|
||||
return monero!.createMoneroRestoreWalletFromSeedCredentials(
|
||||
name: name, height: height, mnemonic: seed, password: password);
|
||||
case WalletType.bitcoin:
|
||||
return bitcoin!.createBitcoinRestoreWalletFromSeedCredentials(
|
||||
name: name, mnemonic: seed, password: password);
|
||||
default:
|
||||
throw Exception('Unexpected type: ${type.toString()}');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<WalletBase> process(WalletCredentials credentials) async =>
|
||||
walletCreationService.restoreFromSeed(credentials);
|
||||
}
|
|
@ -1,7 +1,4 @@
|
|||
import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
||||
import 'package:cake_wallet/core/mnemonic_length.dart';
|
||||
import 'package:cake_wallet/view_model/restore/restore_wallet.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:cake_wallet/ethereum/ethereum.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
|
@ -19,7 +16,6 @@ import 'package:cake_wallet/view_model/restore/restore_mode.dart';
|
|||
|
||||
part 'wallet_restore_view_model.g.dart';
|
||||
|
||||
|
||||
class WalletRestoreViewModel = WalletRestoreViewModelBase
|
||||
with _$WalletRestoreViewModel;
|
||||
|
||||
|
@ -27,11 +23,13 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
|
|||
WalletRestoreViewModelBase(AppStore appStore, WalletCreationService walletCreationService,
|
||||
Box<WalletInfo> walletInfoSource,
|
||||
{required WalletType type})
|
||||
: availableModes = (type == WalletType.monero || type == WalletType.haven)
|
||||
? WalletRestoreMode.values
|
||||
: [WalletRestoreMode.seed],
|
||||
: availableModes =
|
||||
(type == WalletType.monero || type == WalletType.haven || type == WalletType.ethereum)
|
||||
? WalletRestoreMode.values
|
||||
: [WalletRestoreMode.seed],
|
||||
hasSeedLanguageSelector = type == WalletType.monero || type == WalletType.haven,
|
||||
hasBlockchainHeightLanguageSelector = type == WalletType.monero || type == WalletType.haven,
|
||||
hasRestoreFromPrivateKey = type == WalletType.ethereum,
|
||||
isButtonEnabled = false,
|
||||
mode = WalletRestoreMode.seed,
|
||||
super(appStore, walletInfoSource, walletCreationService, type: type, isRecovery: true) {
|
||||
|
@ -47,13 +45,14 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
|
|||
final List<WalletRestoreMode> availableModes;
|
||||
final bool hasSeedLanguageSelector;
|
||||
final bool hasBlockchainHeightLanguageSelector;
|
||||
final bool hasRestoreFromPrivateKey;
|
||||
|
||||
@observable
|
||||
WalletRestoreMode mode;
|
||||
|
||||
@observable
|
||||
bool isButtonEnabled;
|
||||
|
||||
|
||||
@override
|
||||
WalletCredentials getCredentials(dynamic options) {
|
||||
final password = generateWalletPassword();
|
||||
|
@ -97,17 +96,17 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
|
|||
}
|
||||
|
||||
if (mode == WalletRestoreMode.keys) {
|
||||
final viewKey = options['viewKey'] as String;
|
||||
final spendKey = options['spendKey'] as String;
|
||||
final address = options['address'] as String;
|
||||
final viewKey = options['viewKey'] as String?;
|
||||
final spendKey = options['spendKey'] as String?;
|
||||
final address = options['address'] as String?;
|
||||
|
||||
if (type == WalletType.monero) {
|
||||
return monero!.createMoneroRestoreWalletFromKeysCredentials(
|
||||
name: name,
|
||||
height: height,
|
||||
spendKey: spendKey,
|
||||
viewKey: viewKey,
|
||||
address: address,
|
||||
spendKey: spendKey!,
|
||||
viewKey: viewKey!,
|
||||
address: address!,
|
||||
password: password,
|
||||
language: 'English');
|
||||
}
|
||||
|
@ -116,12 +115,20 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
|
|||
return haven!.createHavenRestoreWalletFromKeysCredentials(
|
||||
name: name,
|
||||
height: height,
|
||||
spendKey: spendKey,
|
||||
viewKey: viewKey,
|
||||
address: address,
|
||||
spendKey: spendKey!,
|
||||
viewKey: viewKey!,
|
||||
address: address!,
|
||||
password: password,
|
||||
language: 'English');
|
||||
}
|
||||
|
||||
if (type == WalletType.ethereum) {
|
||||
return ethereum!.createEthereumRestoreWalletFromPrivateKey(
|
||||
name: name,
|
||||
privateKey: options['private_key'] as String,
|
||||
password: password,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
throw Exception('Unexpected type: ${type.toString()}');
|
||||
|
|
|
@ -8,7 +8,7 @@ class WalletSeedViewModel = WalletSeedViewModelBase with _$WalletSeedViewModel;
|
|||
abstract class WalletSeedViewModelBase with Store {
|
||||
WalletSeedViewModelBase(WalletBase wallet)
|
||||
: name = wallet.name,
|
||||
seed = wallet.seed;
|
||||
seed = wallet.seed!;
|
||||
|
||||
@observable
|
||||
String name;
|
||||
|
|
|
@ -57,11 +57,11 @@ DEPENDENCIES:
|
|||
- FlutterMacOS (from `Flutter/ephemeral`)
|
||||
- in_app_review (from `Flutter/ephemeral/.symlinks/plugins/in_app_review/macos`)
|
||||
- package_info (from `Flutter/ephemeral/.symlinks/plugins/package_info/macos`)
|
||||
- path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/macos`)
|
||||
- path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`)
|
||||
- platform_device_id (from `Flutter/ephemeral/.symlinks/plugins/platform_device_id/macos`)
|
||||
- platform_device_id_macos (from `Flutter/ephemeral/.symlinks/plugins/platform_device_id_macos/macos`)
|
||||
- share_plus_macos (from `Flutter/ephemeral/.symlinks/plugins/share_plus_macos/macos`)
|
||||
- shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/macos`)
|
||||
- shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin`)
|
||||
- url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`)
|
||||
- wakelock_macos (from `Flutter/ephemeral/.symlinks/plugins/wakelock_macos/macos`)
|
||||
|
||||
|
@ -87,7 +87,7 @@ EXTERNAL SOURCES:
|
|||
package_info:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/package_info/macos
|
||||
path_provider_foundation:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/macos
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin
|
||||
platform_device_id:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/platform_device_id/macos
|
||||
platform_device_id_macos:
|
||||
|
@ -95,7 +95,7 @@ EXTERNAL SOURCES:
|
|||
share_plus_macos:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/share_plus_macos/macos
|
||||
shared_preferences_foundation:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/macos
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin
|
||||
url_launcher_macos:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos
|
||||
wakelock_macos:
|
||||
|
|
|
@ -104,7 +104,10 @@ dev_dependencies:
|
|||
# check flutter_launcher_icons for usage
|
||||
pedantic: ^1.8.0
|
||||
# replace https://github.com/dart-lang/lints#migrating-from-packagepedantic
|
||||
# translator: ^0.1.7
|
||||
translator:
|
||||
git:
|
||||
url: https://github.com/cake-tech/google-translator.git
|
||||
version: 1.0.0
|
||||
|
||||
flutter_icons:
|
||||
image_path: "assets/images/app_logo.png"
|
||||
|
|
|
@ -680,5 +680,9 @@
|
|||
"support_title_guides": "أدلة محفظة كعكة",
|
||||
"support_description_guides": "توثيق ودعم القضايا المشتركة",
|
||||
"support_title_other_links": "روابط دعم أخرى",
|
||||
"support_description_other_links": "انضم إلى مجتمعاتنا أو تصل إلينا شركائنا من خلال أساليب أخرى"
|
||||
"support_description_other_links": "انضم إلى مجتمعاتنا أو تصل إلينا شركائنا من خلال أساليب أخرى",
|
||||
"select_destination": ".ﻲﻃﺎﻴﺘﺣﻻﺍ ﺦﺴﻨﻟﺍ ﻒﻠﻣ ﺔﻬﺟﻭ ﺪﻳﺪﺤﺗ ءﺎﺟﺮﻟﺍ",
|
||||
"save_to_downloads": "ﺕﻼﻳﺰﻨﺘﻟﺍ ﻲﻓ ﻆﻔﺣ",
|
||||
"support_description_other_links": "انضم إلى مجتمعاتنا أو تصل إلينا شركائنا من خلال أساليب أخرى",
|
||||
"auto_generate_subaddresses": "تلقائي توليد subddresses"
|
||||
}
|
||||
|
|
|
@ -626,18 +626,19 @@
|
|||
"setup_totp_recommended": "Настройка на TOTP (препоръчително)",
|
||||
"disable_buy": "Деактивирайте действието за покупка",
|
||||
"disable_sell": "Деактивирайте действието за продажба",
|
||||
"cake_2fa_preset" : "Торта 2FA Preset",
|
||||
"auto_generate_subaddresses": "Автоматично генериране на подадреси",
|
||||
"cake_2fa_preset": "Торта 2FA Preset",
|
||||
"narrow": "Тесен",
|
||||
"normal": "нормално",
|
||||
"aggressive": "Прекалено усърден",
|
||||
"require_for_assessing_wallet": "Изискване за достъп до портфейла",
|
||||
"require_for_sends_to_non_contacts" : "Изискване за изпращане до лица без контакт",
|
||||
"require_for_sends_to_contacts" : "Изискване за изпращане до контакти",
|
||||
"require_for_sends_to_internal_wallets" : "Изискване за изпращане до вътрешни портфейли",
|
||||
"require_for_exchanges_to_internal_wallets" : "Изискване за обмен към вътрешни портфейли",
|
||||
"require_for_adding_contacts" : "Изисква се за добавяне на контакти",
|
||||
"require_for_creating_new_wallets" : "Изискване за създаване на нови портфейли",
|
||||
"require_for_all_security_and_backup_settings" : "Изисква се за всички настройки за сигурност и архивиране",
|
||||
"require_for_sends_to_non_contacts": "Изискване за изпращане до лица без контакт",
|
||||
"require_for_sends_to_contacts": "Изискване за изпращане до контакти",
|
||||
"require_for_sends_to_internal_wallets": "Изискване за изпращане до вътрешни портфейли",
|
||||
"require_for_exchanges_to_internal_wallets": "Изискване за обмен към вътрешни портфейли",
|
||||
"require_for_adding_contacts": "Изисква се за добавяне на контакти",
|
||||
"require_for_creating_new_wallets": "Изискване за създаване на нови портфейли",
|
||||
"require_for_all_security_and_backup_settings": "Изисква се за всички настройки за сигурност и архивиране",
|
||||
"available_balance_description": "Това е балансът, който можете да използвате за покупка на криптовалути. Това не включва замразените средства.",
|
||||
"syncing_wallet_alert_title": "Вашият портфейл се синхронизира",
|
||||
"syncing_wallet_alert_content": "Списъкът ви с баланс и транзакции може да не е пълен, докато в горната част не пише „СИНХРОНИЗИРАН“. Кликнете/докоснете, за да научите повече.",
|
||||
|
@ -676,5 +677,7 @@
|
|||
"support_title_guides": "Ръководства за портфейл за торта",
|
||||
"support_description_guides": "Документация и подкрепа за общи проблеми",
|
||||
"support_title_other_links": "Други връзки за поддръжка",
|
||||
"support_description_other_links": "Присъединете се към нашите общности или се свържете с нас нашите партньори чрез други методи"
|
||||
"support_description_other_links": "Присъединете се към нашите общности или се свържете с нас нашите партньори чрез други методи",
|
||||
"select_destination": "Моля, изберете дестинация за архивния файл.",
|
||||
"save_to_downloads": "Запазване в Изтегляния"
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue