mirror of
https://github.com/cake-tech/cake_wallet.git
synced 2024-12-22 11:39:22 +00:00
CW-438 add nano (#1015)
* Fix web3dart versioning issue * Add primary receive address extracted from private key * Implement open wallet functionality * Implement restore wallet from seed functionality * Fixate web3dart version as higher versions cause some issues * Add Initial Transaction priorities for eth Add estimated gas price * Rename priority value to tip * Re-order wallet types * Change ethereum node Fix connection issues * Fix estimating gas for priority * Add case for ethereum to fetch it's seeds * Add case for ethereum to request node * Fix Exchange screen initial pairs * Add initial send transaction flow * Add missing configure for ethereum class * Add Eth address initial setup * Fix Private key for Ethereum wallets * Change sign/send transaction flow * - Fix Conflicts with main - Remove unused function from Haven configure.dart * Add build command for ethereum package * Add missing Node list file to pubspec * - Fix balance display - Fix parsing of Ethereum amount - Add more Ethereum Nodes [skip ci] * - Fix extracting Ethereum Private key from seeds - Integrate signing/sending transaction with the send view model * - Update and Fix Conflicts with main * Add Balances for ERC20 tokens * Fix conflicts with main * Add erc20 abi json * Add send erc20 tokens initial function * add missing getHeightByDate in Haven [skip ci] * Allow contacts and wallets from the same tag * Add Shiba Inu icon * Add send ERC-20 tokens initial flow * Add missing import in generated file * Add initial approach for transaction sending for ERC-20 tokens * Refactor signing/sending transactions * Add initial flow for transactions subscription * Refactor signing/sending transactions * Add home settings icon * Fix conflicts with main * Initial flow for home settings * Add logic flow for adding erc20 tokens * Fix initial UI * Finalize UI for Tokens * Integrate UI with Ethereum flow * Add "Enable/Disable" feature for ERC20 tokens * Add initial Erc20 tokens * Add Sorting and Pin Native Token features * Fix price sorting * Sort tokens list as well when Sort criteria changes * - Improve sorting balances flow - Add initial add token from search bar flow * Fix Accounts Popup UI * Fix Pin native token * Fix Enabling/Disabling tokens Fix sorting by fiat once app is opened Improve token availability mechanism * Fix deleting token Fix renaming tokens * Fix issue with search * Add more tokens * - Fix scroll issue - Add ERC20 tokens placeholder image in picker * - Separate and organize default erc20 tokens - Fix scrolling - Add token placeholder images in picker - Sort disabled tokens alphabetically * Change BNB token initial availability [skip ci] * Fix Conflicts with main * Fix Conflicts with main * Add Verse ERC20 token to the initial tokens list * Add rename wallet to Ethereum * Integrate EtherScan API for fetching address transactions Generate Ethereum specific secrets in Ethereum package * Adjust transactions fiat price for ERC20 tokens * Free Up GitHub Actions Ubuntu Runner Disk Space * Free Up GitHub Actions Ubuntu Runner Disk space (trial 2) * Fix Transaction Fee display * Save transaction history * Enhance loading time for erc20 tokens transactions * Minor Fixes and Enhancements * Fix sending erc20 fix block explorer issue * Fix int overflow * Fix transaction amount conversions * Minor: `slow` -> `Slow` [skip-ci] * initial changes * more base config stuff * config changes * successfully builds! * save * successfully add nano wallet * save * seed generation * receive screen + node screen working * tx history working and fiat fixes * balance working * derivation updates * nano-unfinished * sends working * remove fees from send screen, send and receive transactions working * fixes + auto receive incoming txs * fix for scanning QR codes * save * update translations * fixes * more fixes * more strings * small fix * fix github actions workflow * potential fix * potential fix * ci/cd fix * change rep working * seed generation fixes * fixes * save * change rep screen functional * save * banano changes * fixes, start adding ui for PoW * pow node changes * update translations * fix * account changing barely working * save * disable account generation * small fix * save * UI work * save * fixes after merge main * fixes * remove monero stuff, work on derivation ui * lots of fixes + finish up seed derivation * last minute fixes * node related fixes * more fixes * small fix * more fixes * fixes * pretty big refactor for pow, still some bugs * finally works! * get transactions after send * fix * merge conflict fixes * save * fix pow node showing up twice * done * initial changes * small fix * more merge fixes * fixes * more fixes * fix * save * fix manage pow nodes setting appearing on other wallets * fix contact bug * fixes * fiat fixes * save * save * save * save * updates * cleanup * restore fix * fixes * remove deprecated alert * fix * small fix * remove outdated warning * electrum restore fixes * fixes * fixes * fix * derivation fixes * nano fixes pt.1 * nano fixes pt.2 * bip39 fixes * pownode refactor * nodes pages fixes * observer fix * ssl fix * remove old references * remove unused imports * code cleanup * small fix * small potential fix * save * undo all bitcoin related changes * remove dead code * review fixes * more fixes * fix * fix * review fix * small fix * nano derivation and nanoutil fixes * exchange nano fix * nano review fixes pt.1 * nano fixes pt.2 * nano fixes pt.3 * remove old imports + stop using dynamic in di * nanoutil fixes * add nano.dart to gitignore, configure fixes * review fixes, getnanowalletservice removed * fix settings screen, add changeRep to configure.dart, other minor fixes * remove manage_pow_nodes_page, key derivation edge case handled * remove old refs * more small fixes * Generic Enhancements/Minor fixes * review fixes * hopefully final fixes * review fixes * node connection fixes --------- Co-authored-by: OmarHatem <omarh.ismail1@gmail.com> Co-authored-by: Justin Ehrenhofer <justin.ehrenhofer@gmail.com> Co-authored-by: fossephate <fosse@book.local>
This commit is contained in:
parent
6233422643
commit
4c60b178be
134 changed files with 8171 additions and 1055 deletions
1
.github/workflows/pr_test_build.yml
vendored
1
.github/workflows/pr_test_build.yml
vendored
|
@ -92,6 +92,7 @@ jobs:
|
||||||
cd cw_bitcoin && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd ..
|
cd cw_bitcoin && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd ..
|
||||||
cd cw_haven && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd ..
|
cd cw_haven && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd ..
|
||||||
cd cw_ethereum && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd ..
|
cd cw_ethereum && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd ..
|
||||||
|
cd cw_nano && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd ..
|
||||||
flutter packages pub run build_runner build --delete-conflicting-outputs
|
flutter packages pub run build_runner build --delete-conflicting-outputs
|
||||||
|
|
||||||
- name: Add secrets
|
- name: Add secrets
|
||||||
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -124,6 +124,7 @@ lib/bitcoin/bitcoin.dart
|
||||||
lib/monero/monero.dart
|
lib/monero/monero.dart
|
||||||
lib/haven/haven.dart
|
lib/haven/haven.dart
|
||||||
lib/ethereum/ethereum.dart
|
lib/ethereum/ethereum.dart
|
||||||
|
lib/nano/nano.dart
|
||||||
|
|
||||||
ios/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_180.png
|
ios/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_180.png
|
||||||
ios/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_120.png
|
ios/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_120.png
|
||||||
|
|
6
assets/nano_node_list.yml
Normal file
6
assets/nano_node_list.yml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
-
|
||||||
|
uri: rpc.nano.to
|
||||||
|
useSSL: true
|
||||||
|
is_default: true
|
||||||
|
-
|
||||||
|
uri: node.perish.co:9076
|
9
assets/nano_pow_node_list.yml
Normal file
9
assets/nano_pow_node_list.yml
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
-
|
||||||
|
uri: rpc.nano.to
|
||||||
|
useSSL: true
|
||||||
|
is_default: true
|
||||||
|
-
|
||||||
|
uri: workers.perish.co
|
||||||
|
-
|
||||||
|
uri: worker.nanoriver.cc
|
||||||
|
useSSL: true
|
1
configure_cake_wallet_android.sh
Normal file → Executable file
1
configure_cake_wallet_android.sh
Normal file → Executable file
|
@ -7,4 +7,5 @@ cd cw_monero && flutter pub get && flutter packages pub run build_runner build -
|
||||||
cd cw_bitcoin && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd ..
|
cd cw_bitcoin && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd ..
|
||||||
cd cw_haven && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd ..
|
cd cw_haven && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd ..
|
||||||
cd cw_ethereum && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd ..
|
cd cw_ethereum && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd ..
|
||||||
|
cd cw_nano && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd ..
|
||||||
flutter packages pub run build_runner build --delete-conflicting-outputs
|
flutter packages pub run build_runner build --delete-conflicting-outputs
|
||||||
|
|
|
@ -90,6 +90,7 @@ class CryptoCurrency extends EnumerableItem<int> with Serializable<int> implemen
|
||||||
CryptoCurrency.zrx,
|
CryptoCurrency.zrx,
|
||||||
CryptoCurrency.dydx,
|
CryptoCurrency.dydx,
|
||||||
CryptoCurrency.steth,
|
CryptoCurrency.steth,
|
||||||
|
CryptoCurrency.banano,
|
||||||
];
|
];
|
||||||
|
|
||||||
static const havenCurrencies = [
|
static const havenCurrencies = [
|
||||||
|
@ -119,7 +120,7 @@ class CryptoCurrency extends EnumerableItem<int> with Serializable<int> implemen
|
||||||
static const eos = CryptoCurrency(title: 'EOS', fullName: 'EOS', raw: 7, name: 'eos', iconPath: 'assets/images/eos_icon.png');
|
static const eos = CryptoCurrency(title: 'EOS', fullName: 'EOS', raw: 7, name: 'eos', iconPath: 'assets/images/eos_icon.png');
|
||||||
static const eth = CryptoCurrency(title: 'ETH', fullName: 'Ethereum', raw: 8, name: 'eth', iconPath: 'assets/images/eth_icon.png');
|
static const eth = CryptoCurrency(title: 'ETH', fullName: 'Ethereum', raw: 8, name: 'eth', iconPath: 'assets/images/eth_icon.png');
|
||||||
static const ltc = CryptoCurrency(title: 'LTC', fullName: 'Litecoin', raw: 9, name: 'ltc', iconPath: 'assets/images/litecoin-ltc_icon.png');
|
static const ltc = CryptoCurrency(title: 'LTC', fullName: 'Litecoin', raw: 9, name: 'ltc', iconPath: 'assets/images/litecoin-ltc_icon.png');
|
||||||
static const nano = CryptoCurrency(title: 'NANO', raw: 10, name: 'nano', iconPath: 'assets/images/nano.png');
|
static const nano = CryptoCurrency(title: 'XNO', raw: 10, fullName: 'Nano', name: 'xno', iconPath: 'assets/images/nano_icon.png');
|
||||||
static const trx = CryptoCurrency(title: 'TRX', fullName: 'TRON', raw: 11, name: 'trx', iconPath: 'assets/images/trx_icon.png');
|
static const trx = CryptoCurrency(title: 'TRX', fullName: 'TRON', raw: 11, name: 'trx', iconPath: 'assets/images/trx_icon.png');
|
||||||
static const usdt = CryptoCurrency(title: 'USDT', tag: 'OMNI', fullName: 'USDT Tether', raw: 12, name: 'usdt', iconPath: 'assets/images/usdt_icon.png');
|
static const usdt = CryptoCurrency(title: 'USDT', tag: 'OMNI', fullName: 'USDT Tether', raw: 12, name: 'usdt', iconPath: 'assets/images/usdt_icon.png');
|
||||||
static const usdterc20 = CryptoCurrency(title: 'USDT', tag: 'ETH', fullName: 'USDT Tether', raw: 13, name: 'usdterc20', iconPath: 'assets/images/usdterc20_icon.png');
|
static const usdterc20 = CryptoCurrency(title: 'USDT', tag: 'ETH', fullName: 'USDT Tether', raw: 13, name: 'usdterc20', iconPath: 'assets/images/usdterc20_icon.png');
|
||||||
|
@ -198,6 +199,7 @@ class CryptoCurrency extends EnumerableItem<int> with Serializable<int> implemen
|
||||||
static const zrx = CryptoCurrency(title: 'ZRX', tag: 'ETH', fullName: '0x Protocol', raw: 83, name: 'zrx', iconPath: 'assets/images/zrx_icon.png');
|
static const zrx = CryptoCurrency(title: 'ZRX', tag: 'ETH', fullName: '0x Protocol', raw: 83, name: 'zrx', iconPath: 'assets/images/zrx_icon.png');
|
||||||
static const dydx = CryptoCurrency(title: 'DYDX', tag: 'ETH', fullName: 'dYdX', raw: 84, name: 'dydx', iconPath: 'assets/images/dydx_icon.png');
|
static const dydx = CryptoCurrency(title: 'DYDX', tag: 'ETH', fullName: 'dYdX', raw: 84, name: 'dydx', iconPath: 'assets/images/dydx_icon.png');
|
||||||
static const steth = CryptoCurrency(title: 'STETH', tag: 'ETH', fullName: 'Lido Staked Ethereum', raw: 85, name: 'steth', iconPath: 'assets/images/steth_icon.png');
|
static const steth = CryptoCurrency(title: 'STETH', tag: 'ETH', fullName: 'Lido Staked Ethereum', raw: 85, name: 'steth', iconPath: 'assets/images/steth_icon.png');
|
||||||
|
static const banano = CryptoCurrency(title: 'BAN', raw: 86, name: 'banano', iconPath: 'assets/images/nano_icon.png');
|
||||||
|
|
||||||
|
|
||||||
static final Map<int, CryptoCurrency> _rawCurrencyMap =
|
static final Map<int, CryptoCurrency> _rawCurrencyMap =
|
||||||
|
|
|
@ -13,6 +13,10 @@ CryptoCurrency currencyForWalletType(WalletType type) {
|
||||||
return CryptoCurrency.xhv;
|
return CryptoCurrency.xhv;
|
||||||
case WalletType.ethereum:
|
case WalletType.ethereum:
|
||||||
return CryptoCurrency.eth;
|
return CryptoCurrency.eth;
|
||||||
|
case WalletType.nano:
|
||||||
|
return CryptoCurrency.nano;
|
||||||
|
case WalletType.banano:
|
||||||
|
return CryptoCurrency.banano;
|
||||||
default:
|
default:
|
||||||
throw Exception('Unexpected wallet type: ${type.toString()} for CryptoCurrency currencyForWalletType');
|
throw Exception('Unexpected wallet type: ${type.toString()} for CryptoCurrency currencyForWalletType');
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,3 +11,6 @@ const UNSPENT_COINS_INFO_TYPE_ID = 9;
|
||||||
const ANONPAY_INVOICE_INFO_TYPE_ID = 10;
|
const ANONPAY_INVOICE_INFO_TYPE_ID = 10;
|
||||||
const ADDRESS_INFO_TYPE_ID = 11;
|
const ADDRESS_INFO_TYPE_ID = 11;
|
||||||
const ERC20_TOKEN_TYPE_ID = 12;
|
const ERC20_TOKEN_TYPE_ID = 12;
|
||||||
|
const NANO_ACCOUNT_TYPE_ID = 13;
|
||||||
|
const POW_NODE_TYPE_ID = 14;
|
||||||
|
const DERIVATION_TYPE_TYPE_ID = 15;
|
23
cw_core/lib/nano_account.dart
Normal file
23
cw_core/lib/nano_account.dart
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
import 'package:cw_core/hive_type_ids.dart';
|
||||||
|
import 'package:hive/hive.dart';
|
||||||
|
|
||||||
|
part 'nano_account.g.dart';
|
||||||
|
|
||||||
|
@HiveType(typeId: NanoAccount.typeId)
|
||||||
|
class NanoAccount extends HiveObject {
|
||||||
|
NanoAccount({required this.label, required this.id, this.balance, this.isSelected = false});
|
||||||
|
|
||||||
|
static const typeId = NANO_ACCOUNT_TYPE_ID;
|
||||||
|
|
||||||
|
@HiveField(0)
|
||||||
|
String label;
|
||||||
|
|
||||||
|
@HiveField(1)
|
||||||
|
final int id;
|
||||||
|
|
||||||
|
@HiveField(2)
|
||||||
|
bool isSelected;
|
||||||
|
|
||||||
|
@HiveField(3)
|
||||||
|
String? balance;
|
||||||
|
}
|
23
cw_core/lib/nano_account_info_response.dart
Normal file
23
cw_core/lib/nano_account_info_response.dart
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
class AccountInfoResponse {
|
||||||
|
String frontier;
|
||||||
|
int confirmationHeight;
|
||||||
|
String balance;
|
||||||
|
String representative;
|
||||||
|
String? address;
|
||||||
|
|
||||||
|
AccountInfoResponse({
|
||||||
|
required this.frontier,
|
||||||
|
required this.balance,
|
||||||
|
required this.representative,
|
||||||
|
required this.confirmationHeight,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory AccountInfoResponse.fromJson(Map<String, dynamic> json) {
|
||||||
|
return AccountInfoResponse(
|
||||||
|
frontier: json['frontier'] as String,
|
||||||
|
representative: json['representative'] as String,
|
||||||
|
balance: json['balance'] as String,
|
||||||
|
confirmationHeight: int.parse(json['confirmation_height'] as String),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,19 +9,19 @@ import 'package:http/io_client.dart' as ioc;
|
||||||
|
|
||||||
part 'node.g.dart';
|
part 'node.g.dart';
|
||||||
|
|
||||||
Uri createUriFromElectrumAddress(String address) =>
|
Uri createUriFromElectrumAddress(String address) => Uri.tryParse('tcp://$address')!;
|
||||||
Uri.tryParse('tcp://$address')!;
|
|
||||||
|
|
||||||
@HiveType(typeId: Node.typeId)
|
@HiveType(typeId: Node.typeId)
|
||||||
class Node extends HiveObject with Keyable {
|
class Node extends HiveObject with Keyable {
|
||||||
Node(
|
Node({
|
||||||
{this.login,
|
this.login,
|
||||||
this.password,
|
this.password,
|
||||||
this.useSSL,
|
this.useSSL,
|
||||||
this.trusted = false,
|
this.trusted = false,
|
||||||
this.socksProxyAddress,
|
this.socksProxyAddress,
|
||||||
String? uri,
|
String? uri,
|
||||||
WalletType? type,}) {
|
WalletType? type,
|
||||||
|
}) {
|
||||||
if (uri != null) {
|
if (uri != null) {
|
||||||
uriRaw = uri;
|
uriRaw = uri;
|
||||||
}
|
}
|
||||||
|
@ -78,6 +78,13 @@ class Node extends HiveObject with Keyable {
|
||||||
return Uri.http(uriRaw, '');
|
return Uri.http(uriRaw, '');
|
||||||
case WalletType.ethereum:
|
case WalletType.ethereum:
|
||||||
return Uri.https(uriRaw, '');
|
return Uri.https(uriRaw, '');
|
||||||
|
case WalletType.nano:
|
||||||
|
case WalletType.banano:
|
||||||
|
if (isSSL) {
|
||||||
|
return Uri.https(uriRaw, '');
|
||||||
|
} else {
|
||||||
|
return Uri.http(uriRaw, '');
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
throw Exception('Unexpected type ${type.toString()} for Node uri');
|
throw Exception('Unexpected type ${type.toString()} for Node uri');
|
||||||
}
|
}
|
||||||
|
@ -86,13 +93,13 @@ class Node extends HiveObject with Keyable {
|
||||||
@override
|
@override
|
||||||
bool operator ==(other) =>
|
bool operator ==(other) =>
|
||||||
other is Node &&
|
other is Node &&
|
||||||
(other.uriRaw == uriRaw &&
|
(other.uriRaw == uriRaw &&
|
||||||
other.login == login &&
|
other.login == login &&
|
||||||
other.password == password &&
|
other.password == password &&
|
||||||
other.typeRaw == typeRaw &&
|
other.typeRaw == typeRaw &&
|
||||||
other.useSSL == useSSL &&
|
other.useSSL == useSSL &&
|
||||||
other.trusted == trusted &&
|
other.trusted == trusted &&
|
||||||
other.socksProxyAddress == socksProxyAddress);
|
other.socksProxyAddress == socksProxyAddress);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode =>
|
int get hashCode =>
|
||||||
|
@ -120,7 +127,9 @@ class Node extends HiveObject with Keyable {
|
||||||
try {
|
try {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case WalletType.monero:
|
case WalletType.monero:
|
||||||
return useSocksProxy ? requestNodeWithProxy(socksProxyAddress ?? '') : requestMoneroNode();
|
return useSocksProxy
|
||||||
|
? requestNodeWithProxy(socksProxyAddress ?? '')
|
||||||
|
: requestMoneroNode();
|
||||||
case WalletType.bitcoin:
|
case WalletType.bitcoin:
|
||||||
return requestElectrumServer();
|
return requestElectrumServer();
|
||||||
case WalletType.litecoin:
|
case WalletType.litecoin:
|
||||||
|
@ -129,6 +138,9 @@ class Node extends HiveObject with Keyable {
|
||||||
return requestMoneroNode();
|
return requestMoneroNode();
|
||||||
case WalletType.ethereum:
|
case WalletType.ethereum:
|
||||||
return requestElectrumServer();
|
return requestElectrumServer();
|
||||||
|
case WalletType.nano:
|
||||||
|
case WalletType.banano:
|
||||||
|
return requestNanoNode();
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -141,27 +153,23 @@ class Node extends HiveObject with Keyable {
|
||||||
final path = '/json_rpc';
|
final path = '/json_rpc';
|
||||||
final rpcUri = isSSL ? Uri.https(uri.authority, path) : Uri.http(uri.authority, path);
|
final rpcUri = isSSL ? Uri.https(uri.authority, path) : Uri.http(uri.authority, path);
|
||||||
final realm = 'monero-rpc';
|
final realm = 'monero-rpc';
|
||||||
final body = {
|
final body = {'jsonrpc': '2.0', 'id': '0', 'method': 'get_info'};
|
||||||
'jsonrpc': '2.0',
|
|
||||||
'id': '0',
|
|
||||||
'method': 'get_info'
|
|
||||||
};
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final authenticatingClient = HttpClient();
|
final authenticatingClient = HttpClient();
|
||||||
|
|
||||||
authenticatingClient.addCredentials(
|
authenticatingClient.addCredentials(
|
||||||
rpcUri,
|
rpcUri,
|
||||||
realm,
|
realm,
|
||||||
HttpClientDigestCredentials(login ?? '', password ?? ''),
|
HttpClientDigestCredentials(login ?? '', password ?? ''),
|
||||||
);
|
);
|
||||||
|
|
||||||
final http.Client client = ioc.IOClient(authenticatingClient);
|
final http.Client client = ioc.IOClient(authenticatingClient);
|
||||||
|
|
||||||
final response = await client.post(
|
final response = await client.post(
|
||||||
rpcUri,
|
rpcUri,
|
||||||
headers: {'Content-Type': 'application/json'},
|
headers: {'Content-Type': 'application/json'},
|
||||||
body: json.encode(body),
|
body: json.encode(body),
|
||||||
);
|
);
|
||||||
|
|
||||||
client.close();
|
client.close();
|
||||||
|
@ -173,8 +181,24 @@ class Node extends HiveObject with Keyable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> requestNodeWithProxy(String proxy) async {
|
Future<bool> requestNanoNode() async {
|
||||||
|
http.Response response = await http.post(
|
||||||
|
uri,
|
||||||
|
headers: {'Content-type': 'application/json'},
|
||||||
|
body: json.encode(
|
||||||
|
{
|
||||||
|
"action": "block_count",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
if (response.statusCode == 200) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<bool> requestNodeWithProxy(String proxy) async {
|
||||||
if (proxy.isEmpty || !proxy.contains(':')) {
|
if (proxy.isEmpty || !proxy.contains(':')) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,9 +13,7 @@ import 'package:cw_core/sync_status.dart';
|
||||||
import 'package:cw_core/node.dart';
|
import 'package:cw_core/node.dart';
|
||||||
import 'package:cw_core/wallet_type.dart';
|
import 'package:cw_core/wallet_type.dart';
|
||||||
|
|
||||||
abstract class WalletBase<
|
abstract class WalletBase<BalanceType extends Balance, HistoryType extends TransactionHistoryBase,
|
||||||
BalanceType extends Balance,
|
|
||||||
HistoryType extends TransactionHistoryBase,
|
|
||||||
TransactionType extends TransactionInfo> {
|
TransactionType extends TransactionInfo> {
|
||||||
WalletBase(this.walletInfo);
|
WalletBase(this.walletInfo);
|
||||||
|
|
||||||
|
@ -58,6 +56,9 @@ abstract class WalletBase<
|
||||||
|
|
||||||
Future<void> connectToNode({required Node node});
|
Future<void> connectToNode({required Node node});
|
||||||
|
|
||||||
|
// there is a default definition here because only coins with a pow node (nano based) need to override this
|
||||||
|
Future<void> connectToPowNode({required Node node}) async {}
|
||||||
|
|
||||||
Future<void> startSync();
|
Future<void> startSync();
|
||||||
|
|
||||||
Future<PendingTransaction> createTransaction(Object credentials);
|
Future<PendingTransaction> createTransaction(Object credentials);
|
||||||
|
|
|
@ -5,10 +5,15 @@ abstract class WalletCredentials {
|
||||||
required this.name,
|
required this.name,
|
||||||
this.height,
|
this.height,
|
||||||
this.walletInfo,
|
this.walletInfo,
|
||||||
this.password});
|
this.password,
|
||||||
|
this.derivationType,
|
||||||
|
this.derivationPath,
|
||||||
|
});
|
||||||
|
|
||||||
final String name;
|
final String name;
|
||||||
final int? height;
|
final int? height;
|
||||||
String? password;
|
String? password;
|
||||||
|
DerivationType? derivationType;
|
||||||
|
String? derivationPath;
|
||||||
WalletInfo? walletInfo;
|
WalletInfo? walletInfo;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,29 +6,92 @@ import 'package:hive/hive.dart';
|
||||||
|
|
||||||
part 'wallet_info.g.dart';
|
part 'wallet_info.g.dart';
|
||||||
|
|
||||||
|
@HiveType(typeId: DERIVATION_TYPE_TYPE_ID)
|
||||||
|
enum DerivationType {
|
||||||
|
@HiveField(0)
|
||||||
|
unknown,
|
||||||
|
@HiveField(1)
|
||||||
|
def, // default is a reserved word
|
||||||
|
@HiveField(2)
|
||||||
|
nano,
|
||||||
|
@HiveField(3)
|
||||||
|
bip39,
|
||||||
|
@HiveField(4)
|
||||||
|
electrum1,
|
||||||
|
@HiveField(5)
|
||||||
|
electrum2,
|
||||||
|
}
|
||||||
|
|
||||||
|
class DerivationInfo {
|
||||||
|
DerivationInfo({
|
||||||
|
required this.derivationType,
|
||||||
|
this.derivationPath,
|
||||||
|
this.balance = "",
|
||||||
|
this.address = "",
|
||||||
|
this.height = 0,
|
||||||
|
this.script_type,
|
||||||
|
this.description,
|
||||||
|
});
|
||||||
|
|
||||||
|
String balance;
|
||||||
|
String address;
|
||||||
|
int height;
|
||||||
|
final DerivationType derivationType;
|
||||||
|
final String? derivationPath;
|
||||||
|
final String? script_type;
|
||||||
|
final String? description;
|
||||||
|
}
|
||||||
|
|
||||||
@HiveType(typeId: WalletInfo.typeId)
|
@HiveType(typeId: WalletInfo.typeId)
|
||||||
class WalletInfo extends HiveObject {
|
class WalletInfo extends HiveObject {
|
||||||
WalletInfo(this.id, this.name, this.type, this.isRecovery, this.restoreHeight,
|
WalletInfo(
|
||||||
this.timestamp, this.dirPath, this.path, this.address, this.yatEid,
|
this.id,
|
||||||
this.yatLastUsedAddressRaw, this.showIntroCakePayCard)
|
this.name,
|
||||||
|
this.type,
|
||||||
|
this.isRecovery,
|
||||||
|
this.restoreHeight,
|
||||||
|
this.timestamp,
|
||||||
|
this.dirPath,
|
||||||
|
this.path,
|
||||||
|
this.address,
|
||||||
|
this.yatEid,
|
||||||
|
this.yatLastUsedAddressRaw,
|
||||||
|
this.showIntroCakePayCard,
|
||||||
|
this.derivationType,
|
||||||
|
this.derivationPath)
|
||||||
: _yatLastUsedAddressController = StreamController<String>.broadcast();
|
: _yatLastUsedAddressController = StreamController<String>.broadcast();
|
||||||
|
|
||||||
factory WalletInfo.external(
|
factory WalletInfo.external({
|
||||||
{required String id,
|
required String id,
|
||||||
required String name,
|
required String name,
|
||||||
required WalletType type,
|
required WalletType type,
|
||||||
required bool isRecovery,
|
required bool isRecovery,
|
||||||
required int restoreHeight,
|
required int restoreHeight,
|
||||||
required DateTime date,
|
required DateTime date,
|
||||||
required String dirPath,
|
required String dirPath,
|
||||||
required String path,
|
required String path,
|
||||||
required String address,
|
required String address,
|
||||||
bool? showIntroCakePayCard,
|
bool? showIntroCakePayCard,
|
||||||
String yatEid ='',
|
String yatEid = '',
|
||||||
String yatLastUsedAddressRaw = ''}) {
|
String yatLastUsedAddressRaw = '',
|
||||||
return WalletInfo(id, name, type, isRecovery, restoreHeight,
|
DerivationType? derivationType,
|
||||||
date.millisecondsSinceEpoch, dirPath, path, address,
|
String? derivationPath,
|
||||||
yatEid, yatLastUsedAddressRaw, showIntroCakePayCard);
|
}) {
|
||||||
|
return WalletInfo(
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
type,
|
||||||
|
isRecovery,
|
||||||
|
restoreHeight,
|
||||||
|
date.millisecondsSinceEpoch,
|
||||||
|
dirPath,
|
||||||
|
path,
|
||||||
|
address,
|
||||||
|
yatEid,
|
||||||
|
yatLastUsedAddressRaw,
|
||||||
|
showIntroCakePayCard,
|
||||||
|
derivationType,
|
||||||
|
derivationPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const typeId = WALLET_INFO_TYPE_ID;
|
static const typeId = WALLET_INFO_TYPE_ID;
|
||||||
|
@ -79,6 +142,12 @@ class WalletInfo extends HiveObject {
|
||||||
@HiveField(15)
|
@HiveField(15)
|
||||||
List<String>? usedAddresses;
|
List<String>? usedAddresses;
|
||||||
|
|
||||||
|
@HiveField(16)
|
||||||
|
DerivationType? derivationType;
|
||||||
|
|
||||||
|
@HiveField(17)
|
||||||
|
String? derivationPath;
|
||||||
|
|
||||||
String get yatLastUsedAddress => yatLastUsedAddressRaw ?? '';
|
String get yatLastUsedAddress => yatLastUsedAddressRaw ?? '';
|
||||||
|
|
||||||
set yatLastUsedAddress(String address) {
|
set yatLastUsedAddress(String address) {
|
||||||
|
@ -89,7 +158,7 @@ class WalletInfo extends HiveObject {
|
||||||
String get yatEmojiId => yatEid ?? '';
|
String get yatEmojiId => yatEid ?? '';
|
||||||
|
|
||||||
bool get isShowIntroCakePayCard {
|
bool get isShowIntroCakePayCard {
|
||||||
if(showIntroCakePayCard == null) {
|
if (showIntroCakePayCard == null) {
|
||||||
return type != WalletType.haven;
|
return type != WalletType.haven;
|
||||||
}
|
}
|
||||||
return showIntroCakePayCard!;
|
return showIntroCakePayCard!;
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
|
import 'package:cw_core/node.dart';
|
||||||
import 'package:cw_core/wallet_base.dart';
|
import 'package:cw_core/wallet_base.dart';
|
||||||
import 'package:cw_core/wallet_credentials.dart';
|
import 'package:cw_core/wallet_credentials.dart';
|
||||||
|
import 'package:cw_core/wallet_info.dart';
|
||||||
import 'package:cw_core/wallet_type.dart';
|
import 'package:cw_core/wallet_type.dart';
|
||||||
|
|
||||||
abstract class WalletService<N extends WalletCredentials,
|
abstract class WalletService<N extends WalletCredentials, RFS extends WalletCredentials,
|
||||||
RFS extends WalletCredentials, RFK extends WalletCredentials> {
|
RFK extends WalletCredentials> {
|
||||||
WalletType getType();
|
WalletType getType();
|
||||||
|
|
||||||
Future<WalletBase> create(N credentials);
|
Future<WalletBase> create(N credentials);
|
||||||
|
|
|
@ -10,6 +10,8 @@ const walletTypes = [
|
||||||
WalletType.litecoin,
|
WalletType.litecoin,
|
||||||
WalletType.haven,
|
WalletType.haven,
|
||||||
WalletType.ethereum,
|
WalletType.ethereum,
|
||||||
|
WalletType.nano,
|
||||||
|
WalletType.banano,
|
||||||
];
|
];
|
||||||
|
|
||||||
@HiveType(typeId: WALLET_TYPE_TYPE_ID)
|
@HiveType(typeId: WALLET_TYPE_TYPE_ID)
|
||||||
|
@ -31,6 +33,12 @@ enum WalletType {
|
||||||
|
|
||||||
@HiveField(5)
|
@HiveField(5)
|
||||||
ethereum,
|
ethereum,
|
||||||
|
|
||||||
|
@HiveField(6)
|
||||||
|
nano,
|
||||||
|
|
||||||
|
@HiveField(7)
|
||||||
|
banano,
|
||||||
}
|
}
|
||||||
|
|
||||||
int serializeToInt(WalletType type) {
|
int serializeToInt(WalletType type) {
|
||||||
|
@ -45,6 +53,10 @@ int serializeToInt(WalletType type) {
|
||||||
return 3;
|
return 3;
|
||||||
case WalletType.ethereum:
|
case WalletType.ethereum:
|
||||||
return 4;
|
return 4;
|
||||||
|
case WalletType.nano:
|
||||||
|
return 5;
|
||||||
|
case WalletType.banano:
|
||||||
|
return 6;
|
||||||
default:
|
default:
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -62,6 +74,10 @@ WalletType deserializeFromInt(int raw) {
|
||||||
return WalletType.haven;
|
return WalletType.haven;
|
||||||
case 4:
|
case 4:
|
||||||
return WalletType.ethereum;
|
return WalletType.ethereum;
|
||||||
|
case 5:
|
||||||
|
return WalletType.nano;
|
||||||
|
case 6:
|
||||||
|
return WalletType.banano;
|
||||||
default:
|
default:
|
||||||
throw Exception('Unexpected token: $raw for WalletType deserializeFromInt');
|
throw Exception('Unexpected token: $raw for WalletType deserializeFromInt');
|
||||||
}
|
}
|
||||||
|
@ -79,6 +95,10 @@ String walletTypeToString(WalletType type) {
|
||||||
return 'Haven';
|
return 'Haven';
|
||||||
case WalletType.ethereum:
|
case WalletType.ethereum:
|
||||||
return 'Ethereum';
|
return 'Ethereum';
|
||||||
|
case WalletType.nano:
|
||||||
|
return 'Nano';
|
||||||
|
case WalletType.banano:
|
||||||
|
return 'Banano';
|
||||||
default:
|
default:
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
@ -96,6 +116,10 @@ String walletTypeToDisplayName(WalletType type) {
|
||||||
return 'Haven (XHV)';
|
return 'Haven (XHV)';
|
||||||
case WalletType.ethereum:
|
case WalletType.ethereum:
|
||||||
return 'Ethereum (ETH)';
|
return 'Ethereum (ETH)';
|
||||||
|
case WalletType.nano:
|
||||||
|
return 'Nano (XNO)';
|
||||||
|
case WalletType.banano:
|
||||||
|
return 'Banano (BAN)';
|
||||||
default:
|
default:
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
@ -113,6 +137,10 @@ CryptoCurrency walletTypeToCryptoCurrency(WalletType type) {
|
||||||
return CryptoCurrency.xhv;
|
return CryptoCurrency.xhv;
|
||||||
case WalletType.ethereum:
|
case WalletType.ethereum:
|
||||||
return CryptoCurrency.eth;
|
return CryptoCurrency.eth;
|
||||||
|
case WalletType.nano:
|
||||||
|
return CryptoCurrency.nano;
|
||||||
|
case WalletType.banano:
|
||||||
|
return CryptoCurrency.banano;
|
||||||
default:
|
default:
|
||||||
throw Exception('Unexpected wallet type: ${type.toString()} for CryptoCurrency walletTypeToCryptoCurrency');
|
throw Exception('Unexpected wallet type: ${type.toString()} for CryptoCurrency walletTypeToCryptoCurrency');
|
||||||
}
|
}
|
||||||
|
|
|
@ -234,7 +234,6 @@ extern "C"
|
||||||
}
|
}
|
||||||
|
|
||||||
void setUnlocked(bool unlocked);
|
void setUnlocked(bool unlocked);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Monero::Coins *m_coins;
|
Monero::Coins *m_coins;
|
||||||
|
|
20
cw_nano/lib/banano_balance.dart
Normal file
20
cw_nano/lib/banano_balance.dart
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
import 'package:cw_core/balance.dart';
|
||||||
|
import 'package:cw_nano/nano_util.dart';
|
||||||
|
|
||||||
|
class BananoBalance extends Balance {
|
||||||
|
final BigInt currentBalance;
|
||||||
|
final BigInt receivableBalance;
|
||||||
|
|
||||||
|
BananoBalance({required this.currentBalance, required this.receivableBalance}) : super(0, 0) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get formattedAvailableBalance {
|
||||||
|
return NanoUtil.getRawAsUsableString(currentBalance.toString(), NanoUtil.rawPerBanano);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get formattedAdditionalBalance {
|
||||||
|
return NanoUtil.getRawAsUsableString(receivableBalance.toString(), NanoUtil.rawPerBanano);
|
||||||
|
}
|
||||||
|
}
|
7
cw_nano/lib/cw_nano.dart
Normal file
7
cw_nano/lib/cw_nano.dart
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
library cw_nano;
|
||||||
|
|
||||||
|
/// A Calculator.
|
||||||
|
class Calculator {
|
||||||
|
/// Returns [value] plus 1.
|
||||||
|
int addOne(int value) => value + 1;
|
||||||
|
}
|
39
cw_nano/lib/file.dart
Normal file
39
cw_nano/lib/file.dart
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
import 'dart:io';
|
||||||
|
import 'package:cw_core/key.dart';
|
||||||
|
import 'package:encrypt/encrypt.dart' as encrypt;
|
||||||
|
|
||||||
|
Future<void> write(
|
||||||
|
{required String path,
|
||||||
|
required String password,
|
||||||
|
required String data}) async {
|
||||||
|
final keys = extractKeys(password);
|
||||||
|
final key = encrypt.Key.fromBase64(keys.first);
|
||||||
|
final iv = encrypt.IV.fromBase64(keys.last);
|
||||||
|
final encrypted = await encode(key: key, iv: iv, data: data);
|
||||||
|
final f = File(path);
|
||||||
|
f.writeAsStringSync(encrypted);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> writeData(
|
||||||
|
{required String path,
|
||||||
|
required String password,
|
||||||
|
required String data}) async {
|
||||||
|
final keys = extractKeys(password);
|
||||||
|
final key = encrypt.Key.fromBase64(keys.first);
|
||||||
|
final iv = encrypt.IV.fromBase64(keys.last);
|
||||||
|
final encrypted = await encode(key: key, iv: iv, data: data);
|
||||||
|
final f = File(path);
|
||||||
|
f.writeAsStringSync(encrypted);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<String> read({required String path, required String password}) async {
|
||||||
|
final file = File(path);
|
||||||
|
|
||||||
|
if (!file.existsSync()) {
|
||||||
|
file.createSync();
|
||||||
|
}
|
||||||
|
|
||||||
|
final encrypted = file.readAsStringSync();
|
||||||
|
|
||||||
|
return decode(password: password, data: encrypted);
|
||||||
|
}
|
69
cw_nano/lib/nano_account_list.dart
Normal file
69
cw_nano/lib/nano_account_list.dart
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
import 'package:cw_core/cake_hive.dart';
|
||||||
|
import 'package:cw_core/nano_account.dart';
|
||||||
|
import 'package:mobx/mobx.dart';
|
||||||
|
import 'package:hive/hive.dart';
|
||||||
|
|
||||||
|
part 'nano_account_list.g.dart';
|
||||||
|
|
||||||
|
class NanoAccountList = NanoAccountListBase with _$NanoAccountList;
|
||||||
|
|
||||||
|
abstract class NanoAccountListBase with Store {
|
||||||
|
NanoAccountListBase(this.address)
|
||||||
|
: accounts = ObservableList<NanoAccount>(),
|
||||||
|
_isRefreshing = false,
|
||||||
|
_isUpdating = false {
|
||||||
|
refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
@observable
|
||||||
|
ObservableList<NanoAccount> accounts;
|
||||||
|
bool _isRefreshing;
|
||||||
|
bool _isUpdating;
|
||||||
|
|
||||||
|
String address;
|
||||||
|
|
||||||
|
Future<void> update(String? address) async {
|
||||||
|
if (_isUpdating) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
_isUpdating = true;
|
||||||
|
|
||||||
|
final accounts = await getAll(address: address ?? this.address);
|
||||||
|
|
||||||
|
if (accounts.isNotEmpty) {
|
||||||
|
this.accounts.clear();
|
||||||
|
this.accounts.addAll(accounts);
|
||||||
|
}
|
||||||
|
|
||||||
|
_isUpdating = false;
|
||||||
|
} catch (e) {
|
||||||
|
_isUpdating = false;
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<List<NanoAccount>> getAll({String? address}) async {
|
||||||
|
final box = await CakeHive.openBox<NanoAccount>(address ?? this.address);
|
||||||
|
|
||||||
|
// get all accounts in box:
|
||||||
|
return box.values.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> addAccount({required String label}) async {
|
||||||
|
final box = await CakeHive.openBox<NanoAccount>(address);
|
||||||
|
final account = NanoAccount(id: box.length, label: label, balance: "0.00", isSelected: false);
|
||||||
|
await box.add(account);
|
||||||
|
await account.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> setLabelAccount({required int accountIndex, required String label}) async {
|
||||||
|
final box = await CakeHive.openBox<NanoAccount>(address);
|
||||||
|
final account = box.getAt(accountIndex);
|
||||||
|
account!.label = label;
|
||||||
|
await account.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
void refresh() {}
|
||||||
|
}
|
34
cw_nano/lib/nano_balance.dart
Normal file
34
cw_nano/lib/nano_balance.dart
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
import 'package:cw_core/balance.dart';
|
||||||
|
import 'package:cw_nano/nano_util.dart';
|
||||||
|
|
||||||
|
BigInt stringAmountToBigInt(String amount) {
|
||||||
|
return BigInt.parse(NanoUtil.getAmountAsRaw(amount, NanoUtil.rawPerNano));
|
||||||
|
}
|
||||||
|
|
||||||
|
class NanoBalance extends Balance {
|
||||||
|
final BigInt currentBalance;
|
||||||
|
final BigInt receivableBalance;
|
||||||
|
late String formattedCurrentBalance;
|
||||||
|
late String formattedReceivableBalance;
|
||||||
|
|
||||||
|
NanoBalance({required this.currentBalance, required this.receivableBalance}) : super(0, 0) {
|
||||||
|
this.formattedCurrentBalance = "";
|
||||||
|
this.formattedReceivableBalance = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
NanoBalance.fromString(
|
||||||
|
{required this.formattedCurrentBalance, required this.formattedReceivableBalance})
|
||||||
|
: currentBalance = stringAmountToBigInt(formattedCurrentBalance),
|
||||||
|
receivableBalance = stringAmountToBigInt(formattedReceivableBalance),
|
||||||
|
super(0, 0);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get formattedAvailableBalance {
|
||||||
|
return NanoUtil.getRawAsUsableString(currentBalance.toString(), NanoUtil.rawPerNano);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get formattedAdditionalBalance {
|
||||||
|
return NanoUtil.getRawAsUsableString(receivableBalance.toString(), NanoUtil.rawPerNano);
|
||||||
|
}
|
||||||
|
}
|
424
cw_nano/lib/nano_client.dart
Normal file
424
cw_nano/lib/nano_client.dart
Normal file
|
@ -0,0 +1,424 @@
|
||||||
|
import 'dart:async';
|
||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:cw_core/nano_account_info_response.dart';
|
||||||
|
import 'package:cw_nano/nano_balance.dart';
|
||||||
|
import 'package:cw_nano/nano_transaction_model.dart';
|
||||||
|
import 'package:cw_nano/nano_util.dart';
|
||||||
|
import 'package:http/http.dart' as http;
|
||||||
|
import 'package:nanodart/nanodart.dart';
|
||||||
|
import 'package:cw_core/node.dart';
|
||||||
|
|
||||||
|
class NanoClient {
|
||||||
|
static const String DEFAULT_REPRESENTATIVE =
|
||||||
|
"nano_38713x95zyjsqzx6nm1dsom1jmm668owkeb9913ax6nfgj15az3nu8xkx579";
|
||||||
|
|
||||||
|
Node? _node;
|
||||||
|
Node? _powNode;
|
||||||
|
|
||||||
|
bool connect(Node node) {
|
||||||
|
try {
|
||||||
|
_node = node;
|
||||||
|
return true;
|
||||||
|
} catch (e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool connectPow(Node node) {
|
||||||
|
try {
|
||||||
|
_powNode = node;
|
||||||
|
return true;
|
||||||
|
} catch (e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<NanoBalance> getBalance(String address) async {
|
||||||
|
final response = await http.post(
|
||||||
|
_node!.uri,
|
||||||
|
headers: {"Content-Type": "application/json"},
|
||||||
|
body: jsonEncode(
|
||||||
|
{
|
||||||
|
"action": "account_balance",
|
||||||
|
"account": address,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
final data = await jsonDecode(response.body);
|
||||||
|
final String currentBalance = data["balance"] as String;
|
||||||
|
final String receivableBalance = data["receivable"] as String;
|
||||||
|
final BigInt cur = BigInt.parse(currentBalance);
|
||||||
|
final BigInt rec = BigInt.parse(receivableBalance);
|
||||||
|
return NanoBalance(currentBalance: cur, receivableBalance: rec);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<AccountInfoResponse?> getAccountInfo(String address) async {
|
||||||
|
try {
|
||||||
|
final response = await http.post(
|
||||||
|
_node!.uri,
|
||||||
|
headers: {"Content-Type": "application/json"},
|
||||||
|
body: jsonEncode(
|
||||||
|
{
|
||||||
|
"action": "account_info",
|
||||||
|
"representative": "true",
|
||||||
|
"account": address,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
final data = await jsonDecode(response.body);
|
||||||
|
return AccountInfoResponse.fromJson(data as Map<String, dynamic>);
|
||||||
|
} catch (e) {
|
||||||
|
print("error while getting account info");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<String> changeRep({
|
||||||
|
required String privateKey,
|
||||||
|
required String repAddress,
|
||||||
|
required String ourAddress,
|
||||||
|
}) async {
|
||||||
|
try {
|
||||||
|
AccountInfoResponse? accountInfo = await getAccountInfo(ourAddress);
|
||||||
|
|
||||||
|
if (accountInfo == null) {
|
||||||
|
throw Exception("error while getting account info");
|
||||||
|
}
|
||||||
|
|
||||||
|
// construct the change block:
|
||||||
|
Map<String, String> changeBlock = {
|
||||||
|
"type": "state",
|
||||||
|
"account": ourAddress,
|
||||||
|
"previous": accountInfo.frontier,
|
||||||
|
"representative": repAddress,
|
||||||
|
"balance": accountInfo.balance,
|
||||||
|
"link": "0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"link_as_account": "nano_1111111111111111111111111111111111111111111111111111hifc8npp",
|
||||||
|
};
|
||||||
|
|
||||||
|
// sign the change block:
|
||||||
|
final String hash = NanoBlocks.computeStateHash(
|
||||||
|
NanoAccountType.NANO,
|
||||||
|
changeBlock["account"]!,
|
||||||
|
changeBlock["previous"]!,
|
||||||
|
changeBlock["representative"]!,
|
||||||
|
BigInt.parse(changeBlock["balance"]!),
|
||||||
|
changeBlock["link"]!,
|
||||||
|
);
|
||||||
|
final String signature = NanoSignatures.signBlock(hash, privateKey);
|
||||||
|
|
||||||
|
// get PoW for the send block:
|
||||||
|
final String work = await requestWork(accountInfo.frontier);
|
||||||
|
|
||||||
|
changeBlock["signature"] = signature;
|
||||||
|
changeBlock["work"] = work;
|
||||||
|
|
||||||
|
return await processBlock(changeBlock, "change");
|
||||||
|
} catch (e) {
|
||||||
|
throw Exception("error while changing representative");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<String> requestWork(String hash) async {
|
||||||
|
final response = await http.post(
|
||||||
|
_powNode!.uri,
|
||||||
|
headers: {'Content-type': 'application/json'},
|
||||||
|
body: json.encode(
|
||||||
|
{
|
||||||
|
"action": "work_generate",
|
||||||
|
"hash": hash,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
if (response.statusCode == 200) {
|
||||||
|
final Map<String, dynamic> decoded = json.decode(response.body) as Map<String, dynamic>;
|
||||||
|
if (decoded.containsKey("error")) {
|
||||||
|
throw Exception("Received error ${decoded["error"]}");
|
||||||
|
}
|
||||||
|
return decoded["work"] as String;
|
||||||
|
} else {
|
||||||
|
throw Exception("Received work error ${response.body}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<String> send({
|
||||||
|
required String privateKey,
|
||||||
|
required String amountRaw,
|
||||||
|
required String destinationAddress,
|
||||||
|
}) async {
|
||||||
|
final Map<String, String> sendBlock = await constructSendBlock(
|
||||||
|
privateKey: privateKey,
|
||||||
|
amountRaw: amountRaw,
|
||||||
|
destinationAddress: destinationAddress,
|
||||||
|
);
|
||||||
|
|
||||||
|
return await processBlock(sendBlock, "send");
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<String> processBlock(Map<String, String> block, String subtype) async {
|
||||||
|
final headers = {"Content-Type": "application/json"};
|
||||||
|
final processBody = jsonEncode({
|
||||||
|
"action": "process",
|
||||||
|
"json_block": "true",
|
||||||
|
"subtype": subtype,
|
||||||
|
"block": block,
|
||||||
|
});
|
||||||
|
|
||||||
|
final processResponse = await http.post(
|
||||||
|
_node!.uri,
|
||||||
|
headers: headers,
|
||||||
|
body: processBody,
|
||||||
|
);
|
||||||
|
|
||||||
|
final Map<String, dynamic> decoded = json.decode(processResponse.body) as Map<String, dynamic>;
|
||||||
|
if (decoded.containsKey("error")) {
|
||||||
|
throw Exception("Received error ${decoded["error"]}");
|
||||||
|
}
|
||||||
|
|
||||||
|
// return the hash of the transaction:
|
||||||
|
return decoded["hash"].toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Map<String, String>> constructSendBlock({
|
||||||
|
required String privateKey,
|
||||||
|
required String amountRaw,
|
||||||
|
required String destinationAddress,
|
||||||
|
BigInt? balanceAfterTx,
|
||||||
|
String? previousHash,
|
||||||
|
}) async {
|
||||||
|
try {
|
||||||
|
// our address:
|
||||||
|
final String publicAddress = NanoUtil.privateKeyToAddress(privateKey);
|
||||||
|
|
||||||
|
// first get the current account balance:
|
||||||
|
if (balanceAfterTx == null) {
|
||||||
|
final BigInt currentBalance = (await getBalance(publicAddress)).currentBalance;
|
||||||
|
final BigInt txAmount = BigInt.parse(amountRaw);
|
||||||
|
balanceAfterTx = currentBalance - txAmount;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the account info (we need the frontier and representative):
|
||||||
|
AccountInfoResponse? infoResponse = await getAccountInfo(publicAddress);
|
||||||
|
if (infoResponse == null) {
|
||||||
|
throw Exception(
|
||||||
|
"error while getting account info! (we probably don't have an open account yet)");
|
||||||
|
}
|
||||||
|
|
||||||
|
String frontier = infoResponse.frontier;
|
||||||
|
// override if provided:
|
||||||
|
if (previousHash != null) {
|
||||||
|
frontier = previousHash;
|
||||||
|
}
|
||||||
|
final String representative = infoResponse.representative;
|
||||||
|
// link = destination address:
|
||||||
|
final String link = NanoAccounts.extractPublicKey(destinationAddress);
|
||||||
|
final String linkAsAccount = destinationAddress;
|
||||||
|
|
||||||
|
// construct the send block:
|
||||||
|
Map<String, String> sendBlock = {
|
||||||
|
"type": "state",
|
||||||
|
"account": publicAddress,
|
||||||
|
"previous": frontier,
|
||||||
|
"representative": representative,
|
||||||
|
"balance": balanceAfterTx.toString(),
|
||||||
|
"link": link,
|
||||||
|
};
|
||||||
|
|
||||||
|
// sign the send block:
|
||||||
|
final String hash = NanoBlocks.computeStateHash(
|
||||||
|
NanoAccountType.NANO,
|
||||||
|
sendBlock["account"]!,
|
||||||
|
sendBlock["previous"]!,
|
||||||
|
sendBlock["representative"]!,
|
||||||
|
BigInt.parse(sendBlock["balance"]!),
|
||||||
|
sendBlock["link"]!,
|
||||||
|
);
|
||||||
|
final String signature = NanoSignatures.signBlock(hash, privateKey);
|
||||||
|
|
||||||
|
// get PoW for the send block:
|
||||||
|
final String work = await requestWork(frontier);
|
||||||
|
|
||||||
|
sendBlock["link_as_account"] = linkAsAccount;
|
||||||
|
sendBlock["signature"] = signature;
|
||||||
|
sendBlock["work"] = work;
|
||||||
|
|
||||||
|
// ready to post send block:
|
||||||
|
return sendBlock;
|
||||||
|
} catch (e) {
|
||||||
|
print(e);
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> receiveBlock({
|
||||||
|
required String blockHash,
|
||||||
|
required String source,
|
||||||
|
required String amountRaw,
|
||||||
|
required String destinationAddress,
|
||||||
|
required String privateKey,
|
||||||
|
}) async {
|
||||||
|
bool openBlock = false;
|
||||||
|
|
||||||
|
final headers = {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
};
|
||||||
|
|
||||||
|
// first check if the account is open:
|
||||||
|
// get the account info (we need the frontier and representative):
|
||||||
|
AccountInfoResponse? infoData = await getAccountInfo(destinationAddress);
|
||||||
|
String? frontier;
|
||||||
|
String? representative;
|
||||||
|
|
||||||
|
if (infoData == null) {
|
||||||
|
// account is not open yet, we need to create an open block:
|
||||||
|
openBlock = true;
|
||||||
|
// we don't have a representative set yet:
|
||||||
|
representative = DEFAULT_REPRESENTATIVE;
|
||||||
|
// we don't have a frontier yet:
|
||||||
|
frontier = "0000000000000000000000000000000000000000000000000000000000000000";
|
||||||
|
} else {
|
||||||
|
frontier = infoData.frontier;
|
||||||
|
representative = infoData.representative;
|
||||||
|
}
|
||||||
|
|
||||||
|
// first get the account balance:
|
||||||
|
final BigInt currentBalance = (await getBalance(destinationAddress)).currentBalance;
|
||||||
|
final BigInt txAmount = BigInt.parse(amountRaw);
|
||||||
|
final BigInt balanceAfterTx = currentBalance + txAmount;
|
||||||
|
|
||||||
|
// link = send block hash:
|
||||||
|
final String link = blockHash;
|
||||||
|
// this "linkAsAccount" is meaningless:
|
||||||
|
final String linkAsAccount = NanoAccounts.createAccount(NanoAccountType.NANO, blockHash);
|
||||||
|
|
||||||
|
// construct the receive block:
|
||||||
|
Map<String, String> receiveBlock = {
|
||||||
|
"type": "state",
|
||||||
|
"account": destinationAddress,
|
||||||
|
"previous": frontier,
|
||||||
|
"representative": representative,
|
||||||
|
"balance": balanceAfterTx.toString(),
|
||||||
|
"link": link,
|
||||||
|
"link_as_account": linkAsAccount,
|
||||||
|
};
|
||||||
|
|
||||||
|
// sign the receive block:
|
||||||
|
final String hash = NanoBlocks.computeStateHash(
|
||||||
|
NanoAccountType.NANO,
|
||||||
|
receiveBlock["account"]!,
|
||||||
|
receiveBlock["previous"]!,
|
||||||
|
receiveBlock["representative"]!,
|
||||||
|
BigInt.parse(receiveBlock["balance"]!),
|
||||||
|
receiveBlock["link"]!,
|
||||||
|
);
|
||||||
|
final String signature = NanoSignatures.signBlock(hash, privateKey);
|
||||||
|
|
||||||
|
// get PoW for the receive block:
|
||||||
|
String? work;
|
||||||
|
if (openBlock) {
|
||||||
|
work = await requestWork(NanoAccounts.extractPublicKey(destinationAddress));
|
||||||
|
} else {
|
||||||
|
work = await requestWork(frontier);
|
||||||
|
}
|
||||||
|
receiveBlock["link_as_account"] = linkAsAccount;
|
||||||
|
receiveBlock["signature"] = signature;
|
||||||
|
receiveBlock["work"] = work;
|
||||||
|
|
||||||
|
// process the receive block:
|
||||||
|
|
||||||
|
final processBody = jsonEncode({
|
||||||
|
"action": "process",
|
||||||
|
"json_block": "true",
|
||||||
|
"subtype": "receive",
|
||||||
|
"block": receiveBlock,
|
||||||
|
});
|
||||||
|
final processResponse = await http.post(
|
||||||
|
_node!.uri,
|
||||||
|
headers: headers,
|
||||||
|
body: processBody,
|
||||||
|
);
|
||||||
|
|
||||||
|
final Map<String, dynamic> decoded = json.decode(processResponse.body) as Map<String, dynamic>;
|
||||||
|
if (decoded.containsKey("error")) {
|
||||||
|
throw Exception("Received error ${decoded["error"]}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns the number of blocks received:
|
||||||
|
Future<int> confirmAllReceivable({
|
||||||
|
required String destinationAddress,
|
||||||
|
required String privateKey,
|
||||||
|
}) async {
|
||||||
|
final receivableResponse = await http.post(_node!.uri,
|
||||||
|
headers: {"Content-Type": "application/json"},
|
||||||
|
body: jsonEncode({
|
||||||
|
"action": "receivable",
|
||||||
|
"account": destinationAddress,
|
||||||
|
"count": "-1",
|
||||||
|
"source": true,
|
||||||
|
}));
|
||||||
|
|
||||||
|
final receivableData = await jsonDecode(receivableResponse.body);
|
||||||
|
if (receivableData["blocks"] == "" || receivableData["blocks"] == null) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
dynamic blocks;
|
||||||
|
if (receivableData["blocks"] is List<dynamic>) {
|
||||||
|
var listBlocks = receivableData["blocks"] as List<dynamic>;
|
||||||
|
if (listBlocks.isEmpty) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
blocks = {for (var block in listBlocks) block['hash']: block};
|
||||||
|
} else {
|
||||||
|
blocks = receivableData["blocks"] as Map<String, dynamic>;
|
||||||
|
}
|
||||||
|
|
||||||
|
blocks = blocks as Map<String, dynamic>;
|
||||||
|
|
||||||
|
// confirm all receivable blocks:
|
||||||
|
for (final blockHash in blocks.keys) {
|
||||||
|
final block = blocks[blockHash];
|
||||||
|
final String amountRaw = block["amount"] as String;
|
||||||
|
final String source = block["source"] as String;
|
||||||
|
await receiveBlock(
|
||||||
|
blockHash: blockHash,
|
||||||
|
source: source,
|
||||||
|
amountRaw: amountRaw,
|
||||||
|
privateKey: privateKey,
|
||||||
|
destinationAddress: destinationAddress,
|
||||||
|
);
|
||||||
|
// a bit of a hack:
|
||||||
|
await Future<void>.delayed(const Duration(seconds: 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
return blocks.keys.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
void stop() {}
|
||||||
|
|
||||||
|
Future<List<NanoTransactionModel>> fetchTransactions(String address) async {
|
||||||
|
try {
|
||||||
|
final response = await http.post(_node!.uri,
|
||||||
|
headers: {"Content-Type": "application/json"},
|
||||||
|
body: jsonEncode({
|
||||||
|
"action": "account_history",
|
||||||
|
"account": address,
|
||||||
|
"count": "250", // TODO: pick a number
|
||||||
|
// "raw": true,
|
||||||
|
}));
|
||||||
|
final data = await jsonDecode(response.body);
|
||||||
|
final transactions = data["history"] is List ? data["history"] as List<dynamic> : [];
|
||||||
|
|
||||||
|
// Map the transactions list to NanoTransactionModel using the factory
|
||||||
|
// reversed so that the DateTime is correct when local_timestamp is absent
|
||||||
|
return transactions.reversed
|
||||||
|
.map<NanoTransactionModel>((transaction) => NanoTransactionModel.fromJson(transaction))
|
||||||
|
.toList();
|
||||||
|
} catch (e) {
|
||||||
|
print(e);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
2088
cw_nano/lib/nano_mnemonic.dart
Normal file
2088
cw_nano/lib/nano_mnemonic.dart
Normal file
File diff suppressed because it is too large
Load diff
7
cw_nano/lib/nano_transaction_credentials.dart
Normal file
7
cw_nano/lib/nano_transaction_credentials.dart
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
import 'package:cw_core/output_info.dart';
|
||||||
|
|
||||||
|
class NanoTransactionCredentials {
|
||||||
|
NanoTransactionCredentials(this.outputs);
|
||||||
|
|
||||||
|
final List<OutputInfo> outputs;
|
||||||
|
}
|
72
cw_nano/lib/nano_transaction_history.dart
Normal file
72
cw_nano/lib/nano_transaction_history.dart
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:core';
|
||||||
|
import 'package:cw_core/pathForWallet.dart';
|
||||||
|
import 'package:cw_core/wallet_info.dart';
|
||||||
|
import 'package:cw_nano/file.dart';
|
||||||
|
import 'package:mobx/mobx.dart';
|
||||||
|
import 'package:cw_core/transaction_history.dart';
|
||||||
|
import 'package:cw_nano/nano_transaction_info.dart';
|
||||||
|
|
||||||
|
part 'nano_transaction_history.g.dart';
|
||||||
|
const transactionsHistoryFileName = 'transactions.json';
|
||||||
|
|
||||||
|
class NanoTransactionHistory = NanoTransactionHistoryBase with _$NanoTransactionHistory;
|
||||||
|
|
||||||
|
abstract class NanoTransactionHistoryBase
|
||||||
|
extends TransactionHistoryBase<NanoTransactionInfo> with Store {
|
||||||
|
NanoTransactionHistoryBase({required this.walletInfo, required String password})
|
||||||
|
: _password = password {
|
||||||
|
transactions = ObservableMap<String, NanoTransactionInfo>();
|
||||||
|
}
|
||||||
|
|
||||||
|
final WalletInfo walletInfo;
|
||||||
|
String _password;
|
||||||
|
|
||||||
|
Future<void> init() async => await _load();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> save() async {
|
||||||
|
try {
|
||||||
|
final dirPath = await pathForWalletDir(name: walletInfo.name, type: walletInfo.type);
|
||||||
|
final path = '$dirPath/$transactionsHistoryFileName';
|
||||||
|
final data = json.encode({'transactions': transactions});
|
||||||
|
await writeData(path: path, password: _password, data: data);
|
||||||
|
} catch (e) {
|
||||||
|
print('Error while save nano transaction history: ${e.toString()}');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void addOne(NanoTransactionInfo transaction) => transactions[transaction.id] = transaction;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void addMany(Map<String, NanoTransactionInfo> transactions) =>
|
||||||
|
this.transactions.addAll(transactions);
|
||||||
|
|
||||||
|
Future<Map<String, dynamic>> _read() async {
|
||||||
|
final dirPath = await pathForWalletDir(name: walletInfo.name, type: walletInfo.type);
|
||||||
|
final path = '$dirPath/$transactionsHistoryFileName';
|
||||||
|
final content = await read(path: path, password: _password);
|
||||||
|
return json.decode(content) as Map<String, dynamic>;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _load() async {
|
||||||
|
try {
|
||||||
|
final content = await _read();
|
||||||
|
final txs = content['transactions'] as Map<String, dynamic>? ?? {};
|
||||||
|
|
||||||
|
txs.entries.forEach((entry) {
|
||||||
|
final val = entry.value;
|
||||||
|
|
||||||
|
if (val is Map<String, dynamic>) {
|
||||||
|
final tx = NanoTransactionInfo.fromJson(val);
|
||||||
|
_update(tx);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
print(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _update(NanoTransactionInfo transaction) => transactions[transaction.id] = transaction;
|
||||||
|
}
|
70
cw_nano/lib/nano_transaction_info.dart
Normal file
70
cw_nano/lib/nano_transaction_info.dart
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
import 'package:cw_core/format_amount.dart';
|
||||||
|
import 'package:cw_core/transaction_direction.dart';
|
||||||
|
import 'package:cw_core/transaction_info.dart';
|
||||||
|
import 'package:cw_nano/nano_util.dart';
|
||||||
|
|
||||||
|
class NanoTransactionInfo extends TransactionInfo {
|
||||||
|
NanoTransactionInfo({
|
||||||
|
required this.id,
|
||||||
|
required this.height,
|
||||||
|
required this.amountRaw,
|
||||||
|
this.tokenSymbol = "XNO",
|
||||||
|
required this.direction,
|
||||||
|
required this.confirmed,
|
||||||
|
required this.date,
|
||||||
|
required this.confirmations,
|
||||||
|
}) : this.amount = amountRaw.toInt();
|
||||||
|
|
||||||
|
final String id;
|
||||||
|
final int height;
|
||||||
|
final int amount;
|
||||||
|
final BigInt amountRaw;
|
||||||
|
final TransactionDirection direction;
|
||||||
|
final DateTime date;
|
||||||
|
final bool confirmed;
|
||||||
|
final int confirmations;
|
||||||
|
final String tokenSymbol;
|
||||||
|
String? _fiatAmount;
|
||||||
|
|
||||||
|
bool get isPending => !this.confirmed;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String amountFormatted() {
|
||||||
|
final String amt = NanoUtil.getRawAsUsableString(amountRaw.toString(), NanoUtil.rawPerNano);
|
||||||
|
final String acc = NanoUtil.getRawAccuracy(amountRaw.toString(), NanoUtil.rawPerNano);
|
||||||
|
return "$acc$amt $tokenSymbol";
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String fiatAmount() => _fiatAmount ?? '';
|
||||||
|
|
||||||
|
@override
|
||||||
|
void changeFiatAmount(String amount) => _fiatAmount = formatAmount(amount);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String feeFormatted() => "0 XNO";
|
||||||
|
|
||||||
|
factory NanoTransactionInfo.fromJson(Map<String, dynamic> data) {
|
||||||
|
return NanoTransactionInfo(
|
||||||
|
id: data['id'] as String,
|
||||||
|
height: data['height'] as int,
|
||||||
|
amountRaw: BigInt.parse(data['amountRaw'] as String),
|
||||||
|
direction: parseTransactionDirectionFromInt(data['direction'] as int),
|
||||||
|
date: DateTime.fromMillisecondsSinceEpoch(data['date'] as int),
|
||||||
|
confirmed: data['confirmed'] as bool,
|
||||||
|
confirmations: data['confirmations'] as int,
|
||||||
|
tokenSymbol: data['tokenSymbol'] as String,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() => {
|
||||||
|
'id': id,
|
||||||
|
'height': height,
|
||||||
|
'amountRaw': amountRaw.toString(),
|
||||||
|
'direction': direction.index,
|
||||||
|
'date': date.millisecondsSinceEpoch,
|
||||||
|
'confirmed': confirmed,
|
||||||
|
'confirmations': confirmations,
|
||||||
|
'tokenSymbol': tokenSymbol,
|
||||||
|
};
|
||||||
|
}
|
39
cw_nano/lib/nano_transaction_model.dart
Normal file
39
cw_nano/lib/nano_transaction_model.dart
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
class NanoTransactionModel {
|
||||||
|
final DateTime? date;
|
||||||
|
final String hash;
|
||||||
|
final bool confirmed;
|
||||||
|
final String account;
|
||||||
|
final BigInt amount;
|
||||||
|
final int height;
|
||||||
|
final String type;
|
||||||
|
|
||||||
|
NanoTransactionModel({
|
||||||
|
this.date,
|
||||||
|
required this.hash,
|
||||||
|
required this.height,
|
||||||
|
required this.amount,
|
||||||
|
required this.confirmed,
|
||||||
|
required this.type,
|
||||||
|
required this.account,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory NanoTransactionModel.fromJson(dynamic json) {
|
||||||
|
DateTime? localTimestamp;
|
||||||
|
try {
|
||||||
|
localTimestamp = DateTime.fromMillisecondsSinceEpoch(
|
||||||
|
int.parse(json["local_timestamp"] as String) * 1000);
|
||||||
|
} catch (e) {
|
||||||
|
localTimestamp = DateTime.now();
|
||||||
|
}
|
||||||
|
|
||||||
|
return NanoTransactionModel(
|
||||||
|
date: localTimestamp,
|
||||||
|
hash: json["hash"] as String,
|
||||||
|
height: int.parse(json["height"] as String),
|
||||||
|
type: json["type"] as String,
|
||||||
|
amount: BigInt.parse(json["amount"] as String),
|
||||||
|
account: json["account"] as String,
|
||||||
|
confirmed: (json["confirmed"] as String) == "true",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
193
cw_nano/lib/nano_util.dart
Normal file
193
cw_nano/lib/nano_util.dart
Normal file
|
@ -0,0 +1,193 @@
|
||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:convert/convert.dart';
|
||||||
|
import "package:ed25519_hd_key/ed25519_hd_key.dart";
|
||||||
|
import 'package:libcrypto/libcrypto.dart';
|
||||||
|
import 'package:nanodart/nanodart.dart';
|
||||||
|
import 'package:decimal/decimal.dart';
|
||||||
|
|
||||||
|
class NanoUtil {
|
||||||
|
// standard:
|
||||||
|
static String seedToPrivate(String seed, int index) {
|
||||||
|
return NanoKeys.seedToPrivate(seed, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
static String seedToAddress(String seed, int index) {
|
||||||
|
return NanoAccounts.createAccount(
|
||||||
|
NanoAccountType.NANO, privateKeyToPublic(seedToPrivate(seed, index)));
|
||||||
|
}
|
||||||
|
|
||||||
|
static String seedToMnemonic(String seed) {
|
||||||
|
return NanoMnemomics.seedToMnemonic(seed).join(" ");
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<String> mnemonicToSeed(String mnemonic) async {
|
||||||
|
return NanoMnemomics.mnemonicListToSeed(mnemonic.split(' '));
|
||||||
|
}
|
||||||
|
|
||||||
|
static String privateKeyToPublic(String privateKey) {
|
||||||
|
// return NanoHelpers.byteToHex(Ed25519Blake2b.getPubkey(NanoHelpers.hexToBytes(privateKey))!);
|
||||||
|
return NanoKeys.createPublicKey(privateKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
static String addressToPublicKey(String publicAddress) {
|
||||||
|
return NanoAccounts.extractPublicKey(publicAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
// universal:
|
||||||
|
static String privateKeyToAddress(String privateKey) {
|
||||||
|
return NanoAccounts.createAccount(NanoAccountType.NANO, privateKeyToPublic(privateKey));
|
||||||
|
}
|
||||||
|
|
||||||
|
static String publicKeyToAddress(String publicKey) {
|
||||||
|
return NanoAccounts.createAccount(NanoAccountType.NANO, publicKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
// standard + hd:
|
||||||
|
static bool isValidSeed(String seed) {
|
||||||
|
// Ensure seed is 64 or 128 characters long
|
||||||
|
if (seed == null || (seed.length != 64 && seed.length != 128)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Ensure seed only contains hex characters, 0-9;A-F
|
||||||
|
return NanoHelpers.isHexString(seed);
|
||||||
|
}
|
||||||
|
|
||||||
|
// // hd:
|
||||||
|
static Future<String> hdMnemonicListToSeed(List<String> words) async {
|
||||||
|
// if (words.length != 24) {
|
||||||
|
// throw Exception('Expected a 24-word list, got a ${words.length} list');
|
||||||
|
// }
|
||||||
|
final Uint8List salt = Uint8List.fromList(utf8.encode('mnemonic'));
|
||||||
|
final Pbkdf2 hasher = Pbkdf2(iterations: 2048);
|
||||||
|
final String seed = await hasher.sha512(words.join(' '), salt);
|
||||||
|
return seed;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<String> hdSeedToPrivate(String seed, int index) async {
|
||||||
|
List<int> seedBytes = hex.decode(seed);
|
||||||
|
KeyData data = await ED25519_HD_KEY.derivePath("m/44'/165'/$index'", seedBytes);
|
||||||
|
return hex.encode(data.key);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<String> hdSeedToAddress(String seed, int index) async {
|
||||||
|
return NanoAccounts.createAccount(
|
||||||
|
NanoAccountType.NANO, privateKeyToPublic(await hdSeedToPrivate(seed, index)));
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<String> uniSeedToAddress(String seed, int index, String type) {
|
||||||
|
if (type == "standard") {
|
||||||
|
return Future<String>.value(seedToAddress(seed, index));
|
||||||
|
} else if (type == "hd") {
|
||||||
|
return hdSeedToAddress(seed, index);
|
||||||
|
} else {
|
||||||
|
throw Exception('Unknown seed type');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<String> uniSeedToPrivate(String seed, int index, String type) {
|
||||||
|
if (type == "standard") {
|
||||||
|
return Future<String>.value(seedToPrivate(seed, index));
|
||||||
|
} else if (type == "hd") {
|
||||||
|
return hdSeedToPrivate(seed, index);
|
||||||
|
} else {
|
||||||
|
throw Exception('Unknown seed type');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool isValidBip39Seed(String seed) {
|
||||||
|
// Ensure seed is 128 characters long
|
||||||
|
if (seed.length != 128) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Ensure seed only contains hex characters, 0-9;A-F
|
||||||
|
return NanoHelpers.isHexString(seed);
|
||||||
|
}
|
||||||
|
|
||||||
|
// number util:
|
||||||
|
|
||||||
|
static const int maxDecimalDigits = 6; // Max digits after decimal
|
||||||
|
static BigInt rawPerNano = BigInt.parse("1000000000000000000000000000000");
|
||||||
|
static BigInt rawPerNyano = BigInt.parse("1000000000000000000000000");
|
||||||
|
static BigInt rawPerBanano = BigInt.parse("100000000000000000000000000000");
|
||||||
|
static BigInt rawPerXMR = BigInt.parse("1000000000000");
|
||||||
|
static BigInt convertXMRtoNano = BigInt.parse("1000000000000000000");
|
||||||
|
// static BigInt convertXMRtoNano = BigInt.parse("1000000000000000000000000000");
|
||||||
|
|
||||||
|
/// Convert raw to ban and return as BigDecimal
|
||||||
|
///
|
||||||
|
/// @param raw 100000000000000000000000000000
|
||||||
|
/// @return Decimal value 1.000000000000000000000000000000
|
||||||
|
///
|
||||||
|
static Decimal getRawAsDecimal(String? raw, BigInt? rawPerCur) {
|
||||||
|
rawPerCur ??= rawPerNano;
|
||||||
|
final Decimal amount = Decimal.parse(raw.toString());
|
||||||
|
final Decimal result = (amount / Decimal.parse(rawPerCur.toString())).toDecimal();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static String truncateDecimal(Decimal input, {int digits = maxDecimalDigits}) {
|
||||||
|
Decimal bigger = input.shift(digits);
|
||||||
|
bigger = bigger.floor(); // chop off the decimal: 1.059 -> 1.05
|
||||||
|
bigger = bigger.shift(-digits);
|
||||||
|
return bigger.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return raw as a NANO amount.
|
||||||
|
///
|
||||||
|
/// @param raw 100000000000000000000000000000
|
||||||
|
/// @returns 1
|
||||||
|
///
|
||||||
|
static String getRawAsUsableString(String? raw, BigInt rawPerCur) {
|
||||||
|
final String res =
|
||||||
|
truncateDecimal(getRawAsDecimal(raw, rawPerCur), digits: maxDecimalDigits + 9);
|
||||||
|
|
||||||
|
if (raw == null || raw == "0" || raw == "00000000000000000000000000000000") {
|
||||||
|
return "0";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!res.contains(".")) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
final String numAmount = res.split(".")[0];
|
||||||
|
String decAmount = res.split(".")[1];
|
||||||
|
|
||||||
|
// truncate:
|
||||||
|
if (decAmount.length > maxDecimalDigits) {
|
||||||
|
decAmount = decAmount.substring(0, maxDecimalDigits);
|
||||||
|
// remove trailing zeros:
|
||||||
|
decAmount = decAmount.replaceAllMapped(RegExp(r'0+$'), (Match match) => '');
|
||||||
|
if (decAmount.isEmpty) {
|
||||||
|
return numAmount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "$numAmount.$decAmount";
|
||||||
|
}
|
||||||
|
|
||||||
|
static String getRawAccuracy(String? raw, BigInt rawPerCur) {
|
||||||
|
final String rawString = getRawAsUsableString(raw, rawPerCur);
|
||||||
|
final String rawDecimalString = getRawAsDecimal(raw, rawPerCur).toString();
|
||||||
|
|
||||||
|
if (raw == null || raw.isEmpty || raw == "0") {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rawString != rawDecimalString) {
|
||||||
|
return "~";
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return readable string amount as raw string
|
||||||
|
/// @param amount 1.01
|
||||||
|
/// @returns 101000000000000000000000000000
|
||||||
|
///
|
||||||
|
static String getAmountAsRaw(String amount, BigInt rawPerCur) {
|
||||||
|
final Decimal asDecimal = Decimal.parse(amount);
|
||||||
|
final Decimal rawDecimal = Decimal.parse(rawPerCur.toString());
|
||||||
|
return (asDecimal * rawDecimal).toString();
|
||||||
|
}
|
||||||
|
}
|
437
cw_nano/lib/nano_wallet.dart
Normal file
437
cw_nano/lib/nano_wallet.dart
Normal file
|
@ -0,0 +1,437 @@
|
||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:cw_core/cake_hive.dart';
|
||||||
|
import 'package:cw_core/crypto_currency.dart';
|
||||||
|
import 'package:cw_core/nano_account_info_response.dart';
|
||||||
|
import 'package:cw_core/node.dart';
|
||||||
|
import 'package:cw_core/pathForWallet.dart';
|
||||||
|
import 'package:cw_core/pending_transaction.dart';
|
||||||
|
import 'package:cw_core/sync_status.dart';
|
||||||
|
import 'package:cw_core/transaction_direction.dart';
|
||||||
|
import 'package:cw_core/transaction_priority.dart';
|
||||||
|
import 'package:cw_core/wallet_info.dart';
|
||||||
|
import 'package:cw_nano/file.dart';
|
||||||
|
import 'package:cw_core/nano_account.dart';
|
||||||
|
import 'package:cw_nano/nano_balance.dart';
|
||||||
|
import 'package:cw_nano/nano_client.dart';
|
||||||
|
import 'package:cw_nano/nano_transaction_credentials.dart';
|
||||||
|
import 'package:cw_nano/nano_transaction_history.dart';
|
||||||
|
import 'package:cw_nano/nano_transaction_info.dart';
|
||||||
|
import 'package:cw_nano/nano_util.dart';
|
||||||
|
import 'package:cw_nano/nano_wallet_keys.dart';
|
||||||
|
import 'package:cw_nano/pending_nano_transaction.dart';
|
||||||
|
import 'package:mobx/mobx.dart';
|
||||||
|
import 'dart:async';
|
||||||
|
import 'package:cw_nano/nano_wallet_addresses.dart';
|
||||||
|
import 'package:cw_core/wallet_base.dart';
|
||||||
|
import 'package:nanodart/nanodart.dart';
|
||||||
|
import 'package:bip39/bip39.dart' as bip39;
|
||||||
|
|
||||||
|
part 'nano_wallet.g.dart';
|
||||||
|
|
||||||
|
class NanoWallet = NanoWalletBase with _$NanoWallet;
|
||||||
|
|
||||||
|
abstract class NanoWalletBase
|
||||||
|
extends WalletBase<NanoBalance, NanoTransactionHistory, NanoTransactionInfo> with Store {
|
||||||
|
NanoWalletBase({
|
||||||
|
required WalletInfo walletInfo,
|
||||||
|
required String mnemonic,
|
||||||
|
required String password,
|
||||||
|
NanoBalance? initialBalance,
|
||||||
|
}) : syncStatus = NotConnectedSyncStatus(),
|
||||||
|
_password = password,
|
||||||
|
_mnemonic = mnemonic,
|
||||||
|
_derivationType = walletInfo.derivationType!,
|
||||||
|
_isTransactionUpdating = false,
|
||||||
|
_client = NanoClient(),
|
||||||
|
walletAddresses = NanoWalletAddresses(walletInfo),
|
||||||
|
balance = ObservableMap<CryptoCurrency, NanoBalance>.of({
|
||||||
|
CryptoCurrency.nano: initialBalance ??
|
||||||
|
NanoBalance(currentBalance: BigInt.zero, receivableBalance: BigInt.zero)
|
||||||
|
}),
|
||||||
|
super(walletInfo) {
|
||||||
|
this.walletInfo = walletInfo;
|
||||||
|
transactionHistory = NanoTransactionHistory(walletInfo: walletInfo, password: password);
|
||||||
|
if (!CakeHive.isAdapterRegistered(NanoAccount.typeId)) {
|
||||||
|
CakeHive.registerAdapter(NanoAccountAdapter());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final String _mnemonic;
|
||||||
|
final String _password;
|
||||||
|
final DerivationType _derivationType;
|
||||||
|
|
||||||
|
String? _privateKey;
|
||||||
|
String? _publicAddress;
|
||||||
|
String? _seedKey;
|
||||||
|
|
||||||
|
String? _representativeAddress;
|
||||||
|
Timer? _receiveTimer;
|
||||||
|
|
||||||
|
late final NanoClient _client;
|
||||||
|
bool _isTransactionUpdating;
|
||||||
|
|
||||||
|
@override
|
||||||
|
NanoWalletAddresses walletAddresses;
|
||||||
|
|
||||||
|
@override
|
||||||
|
@observable
|
||||||
|
SyncStatus syncStatus;
|
||||||
|
|
||||||
|
@override
|
||||||
|
@observable
|
||||||
|
late ObservableMap<CryptoCurrency, NanoBalance> balance;
|
||||||
|
|
||||||
|
// initialize the different forms of private / public key we'll need:
|
||||||
|
Future<void> init() async {
|
||||||
|
final String type = (_derivationType == DerivationType.nano) ? "standard" : "hd";
|
||||||
|
|
||||||
|
// our "mnemonic" is actually a seedkey:
|
||||||
|
if (!_mnemonic.contains(' ')) {
|
||||||
|
_seedKey = _mnemonic;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_seedKey == null) {
|
||||||
|
if (_derivationType == DerivationType.nano) {
|
||||||
|
_seedKey = bip39.mnemonicToEntropy(_mnemonic).toUpperCase();
|
||||||
|
} else {
|
||||||
|
_seedKey = await NanoUtil.hdMnemonicListToSeed(_mnemonic.split(' '));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_privateKey = await NanoUtil.uniSeedToPrivate(_seedKey!, 0, type);
|
||||||
|
_publicAddress = await NanoUtil.uniSeedToAddress(_seedKey!, 0, type);
|
||||||
|
this.walletInfo.address = _publicAddress!;
|
||||||
|
|
||||||
|
await walletAddresses.init();
|
||||||
|
await transactionHistory.init();
|
||||||
|
await save();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int calculateEstimatedFee(TransactionPriority priority, int? amount) {
|
||||||
|
return 0; // always 0 :)
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> changePassword(String password) {
|
||||||
|
throw UnimplementedError("changePassword");
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void close() {
|
||||||
|
_client.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
@override
|
||||||
|
Future<void> connectToNode({required Node node}) async {
|
||||||
|
try {
|
||||||
|
syncStatus = ConnectingSyncStatus();
|
||||||
|
final isConnected = _client.connect(node);
|
||||||
|
if (!isConnected) {
|
||||||
|
throw Exception("Nano Node connection failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await _updateBalance();
|
||||||
|
await _updateRep();
|
||||||
|
await _receiveAll();
|
||||||
|
} catch (e) {
|
||||||
|
print(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
syncStatus = ConnectedSyncStatus();
|
||||||
|
} catch (e) {
|
||||||
|
print(e);
|
||||||
|
syncStatus = FailedSyncStatus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> connectToPowNode({required Node node}) async {
|
||||||
|
_client.connectPow(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<PendingTransaction> createTransaction(Object credentials) async {
|
||||||
|
credentials = credentials as NanoTransactionCredentials;
|
||||||
|
|
||||||
|
BigInt runningAmount = BigInt.zero;
|
||||||
|
await _updateBalance();
|
||||||
|
BigInt runningBalance = balance[currency]?.currentBalance ?? BigInt.zero;
|
||||||
|
|
||||||
|
final List<Map<String, String>> blocks = [];
|
||||||
|
String? previousHash;
|
||||||
|
|
||||||
|
for (var txOut in credentials.outputs) {
|
||||||
|
late BigInt amt;
|
||||||
|
if (txOut.sendAll) {
|
||||||
|
amt = balance[currency]?.currentBalance ?? BigInt.zero;
|
||||||
|
} else {
|
||||||
|
amt = BigInt.tryParse(
|
||||||
|
NanoUtil.getAmountAsRaw(txOut.cryptoAmount ?? "0", NanoUtil.rawPerNano)) ??
|
||||||
|
BigInt.zero;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (balance[currency]?.currentBalance != null && amt > balance[currency]!.currentBalance) {
|
||||||
|
throw Exception("Trying to send more than entire balance!");
|
||||||
|
}
|
||||||
|
|
||||||
|
runningBalance = runningBalance - amt;
|
||||||
|
|
||||||
|
final block = await _client.constructSendBlock(
|
||||||
|
amountRaw: amt.toString(),
|
||||||
|
destinationAddress: txOut.extractedAddress ?? txOut.address,
|
||||||
|
privateKey: _privateKey!,
|
||||||
|
balanceAfterTx: runningBalance,
|
||||||
|
previousHash: previousHash,
|
||||||
|
);
|
||||||
|
previousHash = NanoBlocks.computeStateHash(
|
||||||
|
NanoAccountType.NANO,
|
||||||
|
block["account"]!,
|
||||||
|
block["previous"]!,
|
||||||
|
block["representative"]!,
|
||||||
|
BigInt.parse(block["balance"]!),
|
||||||
|
block["link"]!,
|
||||||
|
);
|
||||||
|
|
||||||
|
blocks.add(block);
|
||||||
|
runningAmount += amt;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (runningAmount > balance[currency]!.currentBalance || runningBalance < BigInt.zero) {
|
||||||
|
throw Exception(("Trying to send more than entire balance!"));
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
|
||||||
|
return PendingNanoTransaction(
|
||||||
|
amount: runningAmount,
|
||||||
|
id: "",
|
||||||
|
nanoClient: _client,
|
||||||
|
blocks: blocks,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _receiveAll() async {
|
||||||
|
await _updateBalance();
|
||||||
|
int blocksReceived = await this._client.confirmAllReceivable(
|
||||||
|
destinationAddress: _publicAddress!,
|
||||||
|
privateKey: _privateKey!,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (blocksReceived > 0) {
|
||||||
|
await Future<void>.delayed(Duration(seconds: 3));
|
||||||
|
_updateBalance();
|
||||||
|
updateTransactions();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> updateTransactions() async {
|
||||||
|
try {
|
||||||
|
if (_isTransactionUpdating) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_isTransactionUpdating = true;
|
||||||
|
final transactions = await fetchTransactions();
|
||||||
|
transactionHistory.addMany(transactions);
|
||||||
|
await transactionHistory.save();
|
||||||
|
_isTransactionUpdating = false;
|
||||||
|
} catch (_) {
|
||||||
|
_isTransactionUpdating = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Map<String, NanoTransactionInfo>> fetchTransactions() async {
|
||||||
|
String address = _publicAddress!;
|
||||||
|
|
||||||
|
final transactions = await _client.fetchTransactions(address);
|
||||||
|
|
||||||
|
final Map<String, NanoTransactionInfo> result = {};
|
||||||
|
|
||||||
|
for (var transactionModel in transactions) {
|
||||||
|
result[transactionModel.hash] = NanoTransactionInfo(
|
||||||
|
id: transactionModel.hash,
|
||||||
|
amountRaw: transactionModel.amount,
|
||||||
|
height: transactionModel.height,
|
||||||
|
direction: transactionModel.type == "send"
|
||||||
|
? TransactionDirection.outgoing
|
||||||
|
: TransactionDirection.incoming,
|
||||||
|
confirmed: transactionModel.confirmed,
|
||||||
|
date: transactionModel.date ?? DateTime.now(),
|
||||||
|
confirmations: transactionModel.confirmed ? 1 : 0,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
NanoWalletKeys get keys {
|
||||||
|
return NanoWalletKeys(seedKey: _seedKey!);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String? get privateKey => _seedKey!;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> rescan({required int height}) async {
|
||||||
|
updateTransactions();
|
||||||
|
_updateBalance();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> save() async {
|
||||||
|
await walletAddresses.updateAddressesInBox();
|
||||||
|
final path = await makePath();
|
||||||
|
await write(path: path, password: _password, data: toJSON());
|
||||||
|
await transactionHistory.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get seed => _mnemonic;
|
||||||
|
|
||||||
|
String get representative => _representativeAddress ?? "";
|
||||||
|
|
||||||
|
@action
|
||||||
|
@override
|
||||||
|
Future<void> startSync() async {
|
||||||
|
try {
|
||||||
|
syncStatus = AttemptingSyncStatus();
|
||||||
|
await _updateBalance();
|
||||||
|
await updateTransactions();
|
||||||
|
|
||||||
|
_receiveTimer?.cancel();
|
||||||
|
_receiveTimer = Timer.periodic(const Duration(seconds: 15), (timer) async {
|
||||||
|
// get our balance:
|
||||||
|
await _updateBalance();
|
||||||
|
// if we have anything to receive, process it:
|
||||||
|
if (balance[currency]!.receivableBalance > BigInt.zero) {
|
||||||
|
await _receiveAll();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
syncStatus = SyncedSyncStatus();
|
||||||
|
} catch (e) {
|
||||||
|
print(e);
|
||||||
|
syncStatus = FailedSyncStatus();
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<String> makePath() async => pathForWallet(name: walletInfo.name, type: walletInfo.type);
|
||||||
|
|
||||||
|
String toJSON() => json.encode({
|
||||||
|
'seedKey': _seedKey,
|
||||||
|
'mnemonic': _mnemonic,
|
||||||
|
'currentBalance': balance[currency]?.currentBalance.toString() ?? "0",
|
||||||
|
'receivableBalance': balance[currency]?.receivableBalance.toString() ?? "0",
|
||||||
|
'derivationType': _derivationType.toString()
|
||||||
|
});
|
||||||
|
|
||||||
|
static Future<NanoWallet> open({
|
||||||
|
required String name,
|
||||||
|
required String password,
|
||||||
|
required WalletInfo walletInfo,
|
||||||
|
}) async {
|
||||||
|
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 balance = NanoBalance.fromString(
|
||||||
|
formattedCurrentBalance: data['currentBalance'] as String? ?? "0",
|
||||||
|
formattedReceivableBalance: data['receivableBalance'] as String? ?? "0");
|
||||||
|
|
||||||
|
DerivationType derivationType = DerivationType.bip39;
|
||||||
|
if (data['derivationType'] == "DerivationType.nano") {
|
||||||
|
derivationType = DerivationType.nano;
|
||||||
|
}
|
||||||
|
|
||||||
|
walletInfo.derivationType = derivationType;
|
||||||
|
|
||||||
|
return NanoWallet(
|
||||||
|
walletInfo: walletInfo,
|
||||||
|
password: password,
|
||||||
|
mnemonic: mnemonic,
|
||||||
|
initialBalance: balance,
|
||||||
|
);
|
||||||
|
// init() should always be run after this!
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _updateBalance() async {
|
||||||
|
try {
|
||||||
|
balance[currency] = await _client.getBalance(_publicAddress!);
|
||||||
|
} catch (e) {
|
||||||
|
print("Failed to get balance $e");
|
||||||
|
}
|
||||||
|
await save();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _updateRep() async {
|
||||||
|
try {
|
||||||
|
AccountInfoResponse accountInfo = (await _client.getAccountInfo(_publicAddress!))!;
|
||||||
|
_representativeAddress = accountInfo.representative;
|
||||||
|
} catch (e) {
|
||||||
|
// account not found:
|
||||||
|
_representativeAddress = NanoClient.DEFAULT_REPRESENTATIVE;
|
||||||
|
throw Exception("Failed to get representative address $e");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> regenerateAddress() async {
|
||||||
|
final String type = (_derivationType == DerivationType.nano) ? "standard" : "hd";
|
||||||
|
_privateKey =
|
||||||
|
await NanoUtil.uniSeedToPrivate(_seedKey!, this.walletAddresses.account!.id, type);
|
||||||
|
_publicAddress =
|
||||||
|
await NanoUtil.uniSeedToAddress(_seedKey!, this.walletAddresses.account!.id, type);
|
||||||
|
|
||||||
|
this.walletInfo.address = _publicAddress!;
|
||||||
|
this.walletAddresses.address = _publicAddress!;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> changeRep(String address) async {
|
||||||
|
try {
|
||||||
|
final String hash = await _client.changeRep(
|
||||||
|
privateKey: _privateKey!,
|
||||||
|
repAddress: address,
|
||||||
|
ourAddress: _publicAddress!,
|
||||||
|
);
|
||||||
|
if (hash.isNotEmpty) {
|
||||||
|
_representativeAddress = address;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
throw Exception("Failed to change representative address $e");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void>? updateBalance() async => await _updateBalance();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> renameWalletFiles(String newWalletName) async {
|
||||||
|
final currentWalletPath = await pathForWallet(name: walletInfo.name, type: type);
|
||||||
|
final currentWalletFile = File(currentWalletPath);
|
||||||
|
|
||||||
|
final currentDirPath = await pathForWalletDir(name: walletInfo.name, type: type);
|
||||||
|
final currentTransactionsFile = File('$currentDirPath/$transactionsHistoryFileName');
|
||||||
|
|
||||||
|
// Copies current wallet files into new wallet name's dir and files
|
||||||
|
if (currentWalletFile.existsSync()) {
|
||||||
|
final newWalletPath = await pathForWallet(name: newWalletName, type: type);
|
||||||
|
await currentWalletFile.copy(newWalletPath);
|
||||||
|
}
|
||||||
|
if (currentTransactionsFile.existsSync()) {
|
||||||
|
final newDirPath = await pathForWalletDir(name: newWalletName, type: type);
|
||||||
|
await currentTransactionsFile.copy('$newDirPath/$transactionsHistoryFileName');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete old name's dir and files
|
||||||
|
await Directory(currentDirPath).delete(recursive: true);
|
||||||
|
}
|
||||||
|
}
|
50
cw_nano/lib/nano_wallet_addresses.dart
Normal file
50
cw_nano/lib/nano_wallet_addresses.dart
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
import 'package:cw_core/cake_hive.dart';
|
||||||
|
import 'package:cw_core/wallet_addresses.dart';
|
||||||
|
import 'package:cw_core/wallet_info.dart';
|
||||||
|
import 'package:cw_core/nano_account.dart';
|
||||||
|
import 'package:cw_nano/nano_account_list.dart';
|
||||||
|
import 'package:mobx/mobx.dart';
|
||||||
|
|
||||||
|
part 'nano_wallet_addresses.g.dart';
|
||||||
|
|
||||||
|
class NanoWalletAddresses = NanoWalletAddressesBase with _$NanoWalletAddresses;
|
||||||
|
|
||||||
|
abstract class NanoWalletAddressesBase extends WalletAddresses with Store {
|
||||||
|
NanoWalletAddressesBase(WalletInfo walletInfo)
|
||||||
|
: accountList = NanoAccountList(walletInfo.address),
|
||||||
|
address = '',
|
||||||
|
super(walletInfo);
|
||||||
|
@override
|
||||||
|
@observable
|
||||||
|
String address;
|
||||||
|
|
||||||
|
@observable
|
||||||
|
NanoAccount? account;
|
||||||
|
|
||||||
|
NanoAccountList accountList;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> init() async {
|
||||||
|
var box = await CakeHive.openBox<NanoAccount>(walletInfo.address);
|
||||||
|
try {
|
||||||
|
box.getAt(0);
|
||||||
|
} catch (e) {
|
||||||
|
box.add(NanoAccount(id: 0, label: "Primary Account", balance: "0.00"));
|
||||||
|
}
|
||||||
|
|
||||||
|
await accountList.update(walletInfo.address);
|
||||||
|
account = accountList.accounts.first;
|
||||||
|
address = walletInfo.address;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> updateAddressesInBox() async {
|
||||||
|
try {
|
||||||
|
addressesMap.clear();
|
||||||
|
addressesMap[address] = '';
|
||||||
|
await saveAddressesInBox();
|
||||||
|
} catch (e) {
|
||||||
|
print(e.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
41
cw_nano/lib/nano_wallet_creation_credentials.dart
Normal file
41
cw_nano/lib/nano_wallet_creation_credentials.dart
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
import 'package:cw_core/wallet_credentials.dart';
|
||||||
|
import 'package:cw_core/wallet_info.dart';
|
||||||
|
|
||||||
|
class NanoNewWalletCredentials extends WalletCredentials {
|
||||||
|
NanoNewWalletCredentials({required String name, String? password})
|
||||||
|
: super(name: name, password: password);
|
||||||
|
}
|
||||||
|
|
||||||
|
class NanoRestoreWalletFromSeedCredentials extends WalletCredentials {
|
||||||
|
NanoRestoreWalletFromSeedCredentials({
|
||||||
|
required String name,
|
||||||
|
required this.mnemonic,
|
||||||
|
int height = 0,
|
||||||
|
String? password,
|
||||||
|
DerivationType? derivationType,
|
||||||
|
}) : super(
|
||||||
|
name: name,
|
||||||
|
password: password,
|
||||||
|
height: height,
|
||||||
|
derivationType: derivationType,
|
||||||
|
);
|
||||||
|
|
||||||
|
final String mnemonic;
|
||||||
|
}
|
||||||
|
|
||||||
|
class NanoWalletLoadingException implements Exception {
|
||||||
|
@override
|
||||||
|
String toString() => 'Failure to load the wallet.';
|
||||||
|
}
|
||||||
|
|
||||||
|
class NanoRestoreWalletFromKeysCredentials extends WalletCredentials {
|
||||||
|
NanoRestoreWalletFromKeysCredentials({
|
||||||
|
required String name,
|
||||||
|
required String password,
|
||||||
|
required this.seedKey,
|
||||||
|
this.derivationType,
|
||||||
|
}) : super(name: name, password: password);
|
||||||
|
|
||||||
|
final String seedKey;
|
||||||
|
final DerivationType? derivationType;
|
||||||
|
}
|
5
cw_nano/lib/nano_wallet_keys.dart
Normal file
5
cw_nano/lib/nano_wallet_keys.dart
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
class NanoWalletKeys {
|
||||||
|
const NanoWalletKeys({required this.seedKey});
|
||||||
|
|
||||||
|
final String seedKey;
|
||||||
|
}
|
163
cw_nano/lib/nano_wallet_service.dart
Normal file
163
cw_nano/lib/nano_wallet_service.dart
Normal file
|
@ -0,0 +1,163 @@
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:cw_core/pathForWallet.dart';
|
||||||
|
import 'package:cw_core/wallet_base.dart';
|
||||||
|
import 'package:cw_core/wallet_info.dart';
|
||||||
|
import 'package:cw_core/wallet_service.dart';
|
||||||
|
import 'package:cw_core/wallet_type.dart';
|
||||||
|
import 'package:cw_nano/nano_mnemonic.dart' as nm;
|
||||||
|
import 'package:cw_nano/nano_util.dart';
|
||||||
|
import 'package:cw_nano/nano_wallet.dart';
|
||||||
|
import 'package:cw_nano/nano_wallet_creation_credentials.dart';
|
||||||
|
import 'package:hive/hive.dart';
|
||||||
|
import 'package:bip39/bip39.dart' as bip39;
|
||||||
|
import 'package:nanodart/nanodart.dart';
|
||||||
|
|
||||||
|
class NanoWalletService extends WalletService<NanoNewWalletCredentials,
|
||||||
|
NanoRestoreWalletFromSeedCredentials, NanoRestoreWalletFromKeysCredentials> {
|
||||||
|
NanoWalletService(this.walletInfoSource);
|
||||||
|
|
||||||
|
final Box<WalletInfo> walletInfoSource;
|
||||||
|
|
||||||
|
static bool walletFilesExist(String path) =>
|
||||||
|
!File(path).existsSync() && !File('$path.keys').existsSync();
|
||||||
|
|
||||||
|
@override
|
||||||
|
WalletType getType() => WalletType.nano;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<WalletBase> create(NanoNewWalletCredentials credentials) async {
|
||||||
|
// nano standard:
|
||||||
|
DerivationType derivationType = DerivationType.nano;
|
||||||
|
String seedKey = NanoSeeds.generateSeed();
|
||||||
|
String mnemonic = NanoUtil.seedToMnemonic(seedKey);
|
||||||
|
|
||||||
|
credentials.walletInfo!.derivationType = derivationType;
|
||||||
|
|
||||||
|
final wallet = NanoWallet(
|
||||||
|
walletInfo: credentials.walletInfo!,
|
||||||
|
mnemonic: mnemonic,
|
||||||
|
password: credentials.password!,
|
||||||
|
);
|
||||||
|
wallet.init();
|
||||||
|
return wallet;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> remove(String wallet) async {
|
||||||
|
final path = await pathForWalletDir(name: wallet, type: getType());
|
||||||
|
final file = Directory(path);
|
||||||
|
final isExist = file.existsSync();
|
||||||
|
|
||||||
|
if (isExist) {
|
||||||
|
await file.delete(recursive: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
final walletInfo = walletInfoSource.values
|
||||||
|
.firstWhere((info) => info.id == WalletBase.idFor(wallet, getType()));
|
||||||
|
await walletInfoSource.delete(walletInfo.key);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> rename(String currentName, String password, String newName) async {
|
||||||
|
final currentWalletInfo = walletInfoSource.values
|
||||||
|
.firstWhere((info) => info.id == WalletBase.idFor(currentName, getType()));
|
||||||
|
|
||||||
|
String randomWords =
|
||||||
|
(List<String>.from(nm.NanoMnemomics.WORDLIST)..shuffle()).take(24).join(' ');
|
||||||
|
final currentWallet =
|
||||||
|
NanoWallet(walletInfo: currentWalletInfo, password: password, mnemonic: randomWords);
|
||||||
|
|
||||||
|
await currentWallet.renameWalletFiles(newName);
|
||||||
|
|
||||||
|
final newWalletInfo = currentWalletInfo;
|
||||||
|
newWalletInfo.id = WalletBase.idFor(newName, getType());
|
||||||
|
newWalletInfo.name = newName;
|
||||||
|
|
||||||
|
await walletInfoSource.put(currentWalletInfo.key, newWalletInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<NanoWallet> restoreFromKeys(NanoRestoreWalletFromKeysCredentials credentials) async {
|
||||||
|
if (credentials.seedKey.contains(' ')) {
|
||||||
|
throw Exception("Invalid key!");
|
||||||
|
} else {
|
||||||
|
if (credentials.seedKey.length != 64 && credentials.seedKey.length != 128) {
|
||||||
|
throw Exception("Invalid key length!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DerivationType derivationType = credentials.derivationType ?? DerivationType.nano;
|
||||||
|
credentials.walletInfo!.derivationType = derivationType;
|
||||||
|
|
||||||
|
String? mnemonic;
|
||||||
|
|
||||||
|
// we can't derive the mnemonic from the key in all cases, only if it's a "nano" seed
|
||||||
|
if (credentials.seedKey.length == 64) {
|
||||||
|
try {
|
||||||
|
mnemonic = NanoUtil.seedToMnemonic(credentials.seedKey);
|
||||||
|
} catch (e) {
|
||||||
|
throw Exception("Wasn't a valid nano style seed!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final wallet = await NanoWallet(
|
||||||
|
password: credentials.password!,
|
||||||
|
mnemonic: mnemonic ?? credentials.seedKey,
|
||||||
|
walletInfo: credentials.walletInfo!,
|
||||||
|
);
|
||||||
|
await wallet.init();
|
||||||
|
await wallet.save();
|
||||||
|
return wallet;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<NanoWallet> restoreFromSeed(NanoRestoreWalletFromSeedCredentials credentials) async {
|
||||||
|
if (credentials.mnemonic.contains(' ')) {
|
||||||
|
if (!bip39.validateMnemonic(credentials.mnemonic)) {
|
||||||
|
throw nm.NanoMnemonicIsIncorrectException();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!NanoMnemomics.validateMnemonic(credentials.mnemonic.split(' '))) {
|
||||||
|
throw nm.NanoMnemonicIsIncorrectException();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (credentials.mnemonic.length != 64 && credentials.mnemonic.length != 128) {
|
||||||
|
throw Exception("Invalid seed length");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DerivationType derivationType = credentials.derivationType ?? DerivationType.nano;
|
||||||
|
|
||||||
|
credentials.walletInfo!.derivationType = derivationType;
|
||||||
|
|
||||||
|
final wallet = await NanoWallet(
|
||||||
|
password: credentials.password!,
|
||||||
|
mnemonic: credentials.mnemonic,
|
||||||
|
walletInfo: credentials.walletInfo!,
|
||||||
|
);
|
||||||
|
|
||||||
|
await wallet.init();
|
||||||
|
await wallet.save();
|
||||||
|
return wallet;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<bool> isWalletExit(String name) async =>
|
||||||
|
File(await pathForWallet(name: name, type: getType())).existsSync();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<NanoWallet> openWallet(String name, String password) async {
|
||||||
|
final walletInfo =
|
||||||
|
walletInfoSource.values.firstWhere((info) => info.id == WalletBase.idFor(name, getType()));
|
||||||
|
final wallet = await NanoWalletBase.open(
|
||||||
|
name: name,
|
||||||
|
password: password,
|
||||||
|
walletInfo: walletInfo,
|
||||||
|
);
|
||||||
|
|
||||||
|
await wallet.init();
|
||||||
|
await wallet.save();
|
||||||
|
return wallet;
|
||||||
|
}
|
||||||
|
}
|
40
cw_nano/lib/pending_nano_transaction.dart
Normal file
40
cw_nano/lib/pending_nano_transaction.dart
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
import 'package:cw_core/pending_transaction.dart';
|
||||||
|
import 'package:cw_nano/nano_client.dart';
|
||||||
|
import 'package:cw_nano/nano_util.dart';
|
||||||
|
|
||||||
|
class PendingNanoTransaction with PendingTransaction {
|
||||||
|
PendingNanoTransaction({
|
||||||
|
required this.nanoClient,
|
||||||
|
required this.amount,
|
||||||
|
required this.id,
|
||||||
|
required this.blocks,
|
||||||
|
});
|
||||||
|
|
||||||
|
final NanoClient nanoClient;
|
||||||
|
final BigInt amount;
|
||||||
|
final String id;
|
||||||
|
final List<Map<String, String>> blocks;
|
||||||
|
String hex = "unused";
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get amountFormatted {
|
||||||
|
final String amt = NanoUtil.getRawAsUsableString(amount.toString(), NanoUtil.rawPerNano);
|
||||||
|
return amt;
|
||||||
|
}
|
||||||
|
|
||||||
|
String get accurateAmountFormatted {
|
||||||
|
final String amt = NanoUtil.getRawAsUsableString(amount.toString(), NanoUtil.rawPerNano);
|
||||||
|
final String acc = NanoUtil.getRawAccuracy(amount.toString(), NanoUtil.rawPerNano);
|
||||||
|
return "$acc$amt";
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get feeFormatted => "0";
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> commit() async {
|
||||||
|
for (var block in blocks) {
|
||||||
|
await nanoClient.processBlock(block, "send");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
756
cw_nano/pubspec.lock
Normal file
756
cw_nano/pubspec.lock
Normal file
|
@ -0,0 +1,756 @@
|
||||||
|
# Generated by pub
|
||||||
|
# See https://dart.dev/tools/pub/glossary#lockfile
|
||||||
|
packages:
|
||||||
|
_fe_analyzer_shared:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: _fe_analyzer_shared
|
||||||
|
sha256: "4897882604d919befd350648c7f91926a9d5de99e67b455bf0917cc2362f4bb8"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "47.0.0"
|
||||||
|
analyzer:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: analyzer
|
||||||
|
sha256: "690e335554a8385bc9d787117d9eb52c0c03ee207a607e593de3c9d71b1cfe80"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "4.7.0"
|
||||||
|
args:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: args
|
||||||
|
sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.4.2"
|
||||||
|
asn1lib:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: asn1lib
|
||||||
|
sha256: b74e3842a52c61f8819a1ec8444b4de5419b41a7465e69d4aa681445377398b0
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.4.1"
|
||||||
|
async:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: async
|
||||||
|
sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.11.0"
|
||||||
|
bip32:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: bip32
|
||||||
|
sha256: "54787cd7a111e9d37394aabbf53d1fc5e2e0e0af2cd01c459147a97c0e3f8a97"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.0"
|
||||||
|
bip39:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: bip39
|
||||||
|
sha256: de1ee27ebe7d96b84bb3a04a4132a0a3007dcdd5ad27dd14aa87a29d97c45edc
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.6"
|
||||||
|
boolean_selector:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: boolean_selector
|
||||||
|
sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.1"
|
||||||
|
bs58check:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: bs58check
|
||||||
|
sha256: c4a164d42b25c2f6bc88a8beccb9fc7d01440f3c60ba23663a20a70faf484ea9
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.2"
|
||||||
|
build:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: build
|
||||||
|
sha256: "3fbda25365741f8251b39f3917fb3c8e286a96fd068a5a242e11c2012d495777"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.3.1"
|
||||||
|
build_config:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: build_config
|
||||||
|
sha256: bf80fcfb46a29945b423bd9aad884590fb1dc69b330a4d4700cac476af1708d1
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.1"
|
||||||
|
build_daemon:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: build_daemon
|
||||||
|
sha256: "757153e5d9cd88253cb13f28c2fb55a537dc31fefd98137549895b5beb7c6169"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.1.1"
|
||||||
|
build_resolvers:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: build_resolvers
|
||||||
|
sha256: "687cf90a3951affac1bd5f9ecb5e3e90b60487f3d9cdc359bb310f8876bb02a6"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.10"
|
||||||
|
build_runner:
|
||||||
|
dependency: "direct dev"
|
||||||
|
description:
|
||||||
|
name: build_runner
|
||||||
|
sha256: b0a8a7b8a76c493e85f1b84bffa0588859a06197863dba8c9036b15581fd9727
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.3.3"
|
||||||
|
build_runner_core:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: build_runner_core
|
||||||
|
sha256: "0671ad4162ed510b70d0eb4ad6354c249f8429cab4ae7a4cec86bbc2886eb76e"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "7.2.7+1"
|
||||||
|
built_collection:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: built_collection
|
||||||
|
sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "5.1.1"
|
||||||
|
built_value:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: built_value
|
||||||
|
sha256: "598a2a682e2a7a90f08ba39c0aaa9374c5112340f0a2e275f61b59389543d166"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "8.6.1"
|
||||||
|
characters:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: characters
|
||||||
|
sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.3.0"
|
||||||
|
checked_yaml:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: checked_yaml
|
||||||
|
sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.3"
|
||||||
|
clock:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: clock
|
||||||
|
sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.1"
|
||||||
|
code_builder:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: code_builder
|
||||||
|
sha256: "4ad01d6e56db961d29661561effde45e519939fdaeb46c351275b182eac70189"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "4.5.0"
|
||||||
|
collection:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: collection
|
||||||
|
sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.17.1"
|
||||||
|
convert:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: convert
|
||||||
|
sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.1.1"
|
||||||
|
crypto:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: crypto
|
||||||
|
sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.0.3"
|
||||||
|
cw_core:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
path: "../cw_core"
|
||||||
|
relative: true
|
||||||
|
source: path
|
||||||
|
version: "0.0.1"
|
||||||
|
dart_style:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: dart_style
|
||||||
|
sha256: "7a03456c3490394c8e7665890333e91ae8a49be43542b616e414449ac358acd4"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.2.4"
|
||||||
|
decimal:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: decimal
|
||||||
|
sha256: "24a261d5d5c87e86c7651c417a5dbdf8bcd7080dd592533910e8d0505a279f21"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.3.3"
|
||||||
|
ed25519_hd_key:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: ed25519_hd_key
|
||||||
|
sha256: "326608234e986ea826a5db4cf4cd6826058d860875a3fff7926c0725fe1a604d"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.2.0"
|
||||||
|
encrypt:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: encrypt
|
||||||
|
sha256: "4fd4e4fdc21b9d7d4141823e1e6515cd94e7b8d84749504c232999fba25d9bbb"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "5.0.1"
|
||||||
|
fake_async:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: fake_async
|
||||||
|
sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.3.1"
|
||||||
|
ffi:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: ffi
|
||||||
|
sha256: ed5337a5660c506388a9f012be0288fb38b49020ce2b45fe1f8b8323fe429f99
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.2"
|
||||||
|
file:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: file
|
||||||
|
sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "6.1.4"
|
||||||
|
fixnum:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: fixnum
|
||||||
|
sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.0"
|
||||||
|
fixnum_nanodart:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: fixnum_nanodart
|
||||||
|
sha256: "4b0132d11ecddc0d2ca64b6d7dee6726db432ed02cac1349d7532a08be5c54fc"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.0"
|
||||||
|
flutter:
|
||||||
|
dependency: "direct main"
|
||||||
|
description: flutter
|
||||||
|
source: sdk
|
||||||
|
version: "0.0.0"
|
||||||
|
flutter_mobx:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_mobx
|
||||||
|
sha256: "0da4add0016387a7bf309a0d0c41d36c6b3ae25ed7a176409267f166509e723e"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.6+5"
|
||||||
|
flutter_test:
|
||||||
|
dependency: "direct dev"
|
||||||
|
description: flutter
|
||||||
|
source: sdk
|
||||||
|
version: "0.0.0"
|
||||||
|
frontend_server_client:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: frontend_server_client
|
||||||
|
sha256: "408e3ca148b31c20282ad6f37ebfa6f4bdc8fede5b74bc2f08d9d92b55db3612"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.2.0"
|
||||||
|
glob:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: glob
|
||||||
|
sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.2"
|
||||||
|
graphs:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: graphs
|
||||||
|
sha256: aedc5a15e78fc65a6e23bcd927f24c64dd995062bcd1ca6eda65a3cff92a4d19
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.3.1"
|
||||||
|
hex:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: hex
|
||||||
|
sha256: "4e7cd54e4b59ba026432a6be2dd9d96e4c5205725194997193bf871703b82c4a"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.2.0"
|
||||||
|
hive:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: hive
|
||||||
|
sha256: "8dcf6db979d7933da8217edcec84e9df1bdb4e4edc7fc77dbd5aa74356d6d941"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.2.3"
|
||||||
|
hive_generator:
|
||||||
|
dependency: "direct dev"
|
||||||
|
description:
|
||||||
|
name: hive_generator
|
||||||
|
sha256: "81fd20125cb2ce8fd23623d7744ffbaf653aae93706c9bd3bf7019ea0ace3938"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.3"
|
||||||
|
http:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: http
|
||||||
|
sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.0"
|
||||||
|
http_multi_server:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: http_multi_server
|
||||||
|
sha256: "97486f20f9c2f7be8f514851703d0119c3596d14ea63227af6f7a481ef2b2f8b"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.2.1"
|
||||||
|
http_parser:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: http_parser
|
||||||
|
sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "4.0.2"
|
||||||
|
intl:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: intl
|
||||||
|
sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.18.1"
|
||||||
|
io:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: io
|
||||||
|
sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.4"
|
||||||
|
js:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: js
|
||||||
|
sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.6.7"
|
||||||
|
json_annotation:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: json_annotation
|
||||||
|
sha256: b10a7b2ff83d83c777edba3c6a0f97045ddadd56c944e1a23a3fdf43a1bf4467
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "4.8.1"
|
||||||
|
libcrypto:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: libcrypto
|
||||||
|
sha256: "18a97db8d88147b0b60d2755f29b5e4944181c4c1a9f52bd1ecbea1b0a5aab03"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.2.2"
|
||||||
|
logging:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: logging
|
||||||
|
sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.2.0"
|
||||||
|
matcher:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: matcher
|
||||||
|
sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.12.15"
|
||||||
|
material_color_utilities:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: material_color_utilities
|
||||||
|
sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.2.0"
|
||||||
|
meta:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: meta
|
||||||
|
sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.9.1"
|
||||||
|
mime:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: mime
|
||||||
|
sha256: e4ff8e8564c03f255408decd16e7899da1733852a9110a58fe6d1b817684a63e
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.4"
|
||||||
|
mobx:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: mobx
|
||||||
|
sha256: "0afcf88b3ee9d6819890bf16c11a727fc8c62cf736fda8e5d3b9b4eace4e62ea"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.2.0"
|
||||||
|
mobx_codegen:
|
||||||
|
dependency: "direct dev"
|
||||||
|
description:
|
||||||
|
name: mobx_codegen
|
||||||
|
sha256: d4beb9cea4b7b014321235f8fdc7c2193ee0fe1d1198e9da7403f8bc85c4407c
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.3.0"
|
||||||
|
nanodart:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: nanodart
|
||||||
|
sha256: "4b2f42d60307b54e8cf384d6193a567d07f8efd773858c0d5948246153c13282"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.0"
|
||||||
|
package_config:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: package_config
|
||||||
|
sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.0"
|
||||||
|
path:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path
|
||||||
|
sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.8.3"
|
||||||
|
path_provider:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path_provider
|
||||||
|
sha256: "3087813781ab814e4157b172f1a11c46be20179fcc9bea043e0fba36bc0acaa2"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.15"
|
||||||
|
path_provider_android:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path_provider_android
|
||||||
|
sha256: "2cec049d282c7f13c594b4a73976b0b4f2d7a1838a6dd5aaf7bd9719196bee86"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.27"
|
||||||
|
path_provider_foundation:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path_provider_foundation
|
||||||
|
sha256: "916731ccbdce44d545414dd9961f26ba5fbaa74bcbb55237d8e65a623a8c7297"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.2.4"
|
||||||
|
path_provider_linux:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path_provider_linux
|
||||||
|
sha256: ffbb8cc9ed2c9ec0e4b7a541e56fd79b138e8f47d2fb86815f15358a349b3b57
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.11"
|
||||||
|
path_provider_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path_provider_platform_interface
|
||||||
|
sha256: "57585299a729335f1298b43245842678cb9f43a6310351b18fb577d6e33165ec"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.6"
|
||||||
|
path_provider_windows:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path_provider_windows
|
||||||
|
sha256: "1cb68ba4cd3a795033de62ba1b7b4564dace301f952de6bfb3cd91b202b6ee96"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.7"
|
||||||
|
pinenacl:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: pinenacl
|
||||||
|
sha256: e5fb0bce1717b7f136f35ee98b5c02b3e6383211f8a77ca882fa7812232a07b9
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.3.4"
|
||||||
|
platform:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: platform
|
||||||
|
sha256: "4a451831508d7d6ca779f7ac6e212b4023dd5a7d08a27a63da33756410e32b76"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.1.0"
|
||||||
|
plugin_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: plugin_platform_interface
|
||||||
|
sha256: "43798d895c929056255600343db8f049921cbec94d31ec87f1dc5c16c01935dd"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.5"
|
||||||
|
pointycastle:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: pointycastle
|
||||||
|
sha256: "7c1e5f0d23c9016c5bbd8b1473d0d3fb3fc851b876046039509e18e0c7485f2c"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.7.3"
|
||||||
|
pool:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: pool
|
||||||
|
sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.5.1"
|
||||||
|
pub_semver:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: pub_semver
|
||||||
|
sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.4"
|
||||||
|
pubspec_parse:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: pubspec_parse
|
||||||
|
sha256: c63b2876e58e194e4b0828fcb080ad0e06d051cb607a6be51a9e084f47cb9367
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.2.3"
|
||||||
|
rational:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: rational
|
||||||
|
sha256: ba58e9e18df9abde280e8b10051e4bce85091e41e8e7e411b6cde2e738d357cf
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.2.2"
|
||||||
|
shelf:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shelf
|
||||||
|
sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.4.1"
|
||||||
|
shelf_web_socket:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shelf_web_socket
|
||||||
|
sha256: "9ca081be41c60190ebcb4766b2486a7d50261db7bd0f5d9615f2d653637a84c1"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.4"
|
||||||
|
sky_engine:
|
||||||
|
dependency: transitive
|
||||||
|
description: flutter
|
||||||
|
source: sdk
|
||||||
|
version: "0.0.99"
|
||||||
|
source_gen:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: source_gen
|
||||||
|
sha256: "2d79738b6bbf38a43920e2b8d189e9a3ce6cc201f4b8fc76be5e4fe377b1c38d"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.2.6"
|
||||||
|
source_helper:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: source_helper
|
||||||
|
sha256: "3b67aade1d52416149c633ba1bb36df44d97c6b51830c2198e934e3fca87ca1f"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.3.3"
|
||||||
|
source_span:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: source_span
|
||||||
|
sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.9.1"
|
||||||
|
stack_trace:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: stack_trace
|
||||||
|
sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.11.0"
|
||||||
|
stream_channel:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: stream_channel
|
||||||
|
sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.1"
|
||||||
|
stream_transform:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: stream_transform
|
||||||
|
sha256: "14a00e794c7c11aa145a170587321aedce29769c08d7f58b1d141da75e3b1c6f"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.0"
|
||||||
|
string_scanner:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: string_scanner
|
||||||
|
sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.2.0"
|
||||||
|
term_glyph:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: term_glyph
|
||||||
|
sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.2.1"
|
||||||
|
test_api:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: test_api
|
||||||
|
sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.5.1"
|
||||||
|
timing:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: timing
|
||||||
|
sha256: "70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.1"
|
||||||
|
typed_data:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: typed_data
|
||||||
|
sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.3.2"
|
||||||
|
vector_math:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: vector_math
|
||||||
|
sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.4"
|
||||||
|
watcher:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: watcher
|
||||||
|
sha256: "6a7f46926b01ce81bfc339da6a7f20afbe7733eff9846f6d6a5466aa4c6667c0"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.2"
|
||||||
|
web_socket_channel:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: web_socket_channel
|
||||||
|
sha256: d88238e5eac9a42bb43ca4e721edba3c08c6354d4a53063afaa568516217621b
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.4.0"
|
||||||
|
win32:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: win32
|
||||||
|
sha256: "5a751eddf9db89b3e5f9d50c20ab8612296e4e8db69009788d6c8b060a84191c"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "4.1.4"
|
||||||
|
xdg_directories:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: xdg_directories
|
||||||
|
sha256: e0b1147eec179d3911f1f19b59206448f78195ca1d20514134e10641b7d7fbff
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.1"
|
||||||
|
yaml:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: yaml
|
||||||
|
sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.1.2"
|
||||||
|
sdks:
|
||||||
|
dart: ">=3.0.0 <4.0.0"
|
||||||
|
flutter: ">=3.3.0"
|
69
cw_nano/pubspec.yaml
Normal file
69
cw_nano/pubspec.yaml
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
name: cw_nano
|
||||||
|
description: A new Flutter package project.
|
||||||
|
version: 0.0.1
|
||||||
|
publish_to: none
|
||||||
|
author: Cake Wallet
|
||||||
|
homepage: https://cakewallet.com
|
||||||
|
|
||||||
|
environment:
|
||||||
|
sdk: '>=2.18.2 <3.0.0'
|
||||||
|
flutter: ">=1.17.0"
|
||||||
|
|
||||||
|
dependencies:
|
||||||
|
flutter:
|
||||||
|
sdk: flutter
|
||||||
|
mobx: ^2.0.7+4
|
||||||
|
bip39: ^1.0.6
|
||||||
|
bip32: ^2.0.0
|
||||||
|
nanodart: ^2.0.0
|
||||||
|
decimal: ^2.3.3
|
||||||
|
libcrypto: ^0.2.2
|
||||||
|
ed25519_hd_key: ^2.2.0
|
||||||
|
hex: ^0.2.0
|
||||||
|
http: ^1.1.0
|
||||||
|
cw_core:
|
||||||
|
path: ../cw_core
|
||||||
|
|
||||||
|
dev_dependencies:
|
||||||
|
flutter_test:
|
||||||
|
sdk: flutter
|
||||||
|
build_runner: ^2.1.11
|
||||||
|
mobx_codegen: ^2.0.7
|
||||||
|
hive_generator: ^1.1.3
|
||||||
|
|
||||||
|
# For information on the generic Dart part of this file, see the
|
||||||
|
# following page: https://dart.dev/tools/pub/pubspec
|
||||||
|
|
||||||
|
# The following section is specific to Flutter packages.
|
||||||
|
flutter:
|
||||||
|
|
||||||
|
# To add assets to your package, add an assets section, like this:
|
||||||
|
# assets:
|
||||||
|
# - images/a_dot_burr.jpeg
|
||||||
|
# - images/a_dot_ham.jpeg
|
||||||
|
#
|
||||||
|
# For details regarding assets in packages, see
|
||||||
|
# https://flutter.dev/assets-and-images/#from-packages
|
||||||
|
#
|
||||||
|
# An image asset can refer to one or more resolution-specific "variants", see
|
||||||
|
# https://flutter.dev/assets-and-images/#resolution-aware
|
||||||
|
|
||||||
|
# To add custom fonts to your package, add a fonts section here,
|
||||||
|
# in this "flutter" section. Each entry in this list should have a
|
||||||
|
# "family" key with the font family name, and a "fonts" key with a
|
||||||
|
# list giving the asset and other descriptors for the font. For
|
||||||
|
# example:
|
||||||
|
# fonts:
|
||||||
|
# - family: Schyler
|
||||||
|
# fonts:
|
||||||
|
# - asset: fonts/Schyler-Regular.ttf
|
||||||
|
# - asset: fonts/Schyler-Italic.ttf
|
||||||
|
# style: italic
|
||||||
|
# - family: Trajan Pro
|
||||||
|
# fonts:
|
||||||
|
# - asset: fonts/TrajanPro.ttf
|
||||||
|
# - asset: fonts/TrajanPro_Bold.ttf
|
||||||
|
# weight: 700
|
||||||
|
#
|
||||||
|
# For details regarding fonts in packages, see
|
||||||
|
# https://flutter.dev/custom-fonts/#from-packages
|
12
cw_nano/test/cw_nano_test.dart
Normal file
12
cw_nano/test/cw_nano_test.dart
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
|
||||||
|
import 'package:cw_nano/cw_nano.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
test('adds one to input values', () {
|
||||||
|
final calculator = Calculator();
|
||||||
|
expect(calculator.addOne(2), 3);
|
||||||
|
expect(calculator.addOne(-7), -6);
|
||||||
|
expect(calculator.addOne(0), 1);
|
||||||
|
});
|
||||||
|
}
|
|
@ -27,6 +27,8 @@ class OnRamperBuyProvider {
|
||||||
return "LTC_LITECOIN";
|
return "LTC_LITECOIN";
|
||||||
case CryptoCurrency.xmr:
|
case CryptoCurrency.xmr:
|
||||||
return "XMR_MONERO";
|
return "XMR_MONERO";
|
||||||
|
case CryptoCurrency.nano:
|
||||||
|
return "XNO_NANO";
|
||||||
default:
|
default:
|
||||||
return _wallet.currency.title;
|
return _wallet.currency.title;
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,8 @@ class AddressValidator extends TextValidator {
|
||||||
return '^3[0-9a-zA-Z]{32}\$|^3[0-9a-zA-Z]{33}\$|^bc1[0-9a-zA-Z]{59}\$';
|
return '^3[0-9a-zA-Z]{32}\$|^3[0-9a-zA-Z]{33}\$|^bc1[0-9a-zA-Z]{59}\$';
|
||||||
case CryptoCurrency.nano:
|
case CryptoCurrency.nano:
|
||||||
return '[0-9a-zA-Z_]';
|
return '[0-9a-zA-Z_]';
|
||||||
|
case CryptoCurrency.banano:
|
||||||
|
return '[0-9a-zA-Z_]';
|
||||||
case CryptoCurrency.usdc:
|
case CryptoCurrency.usdc:
|
||||||
case CryptoCurrency.usdcpoly:
|
case CryptoCurrency.usdcpoly:
|
||||||
case CryptoCurrency.ape:
|
case CryptoCurrency.ape:
|
||||||
|
@ -177,6 +179,8 @@ class AddressValidator extends TextValidator {
|
||||||
return [34, 43, 63];
|
return [34, 43, 63];
|
||||||
case CryptoCurrency.nano:
|
case CryptoCurrency.nano:
|
||||||
return [64, 65];
|
return [64, 65];
|
||||||
|
case CryptoCurrency.banano:
|
||||||
|
return [64, 65];
|
||||||
case CryptoCurrency.sc:
|
case CryptoCurrency.sc:
|
||||||
return [76];
|
return [76];
|
||||||
case CryptoCurrency.sol:
|
case CryptoCurrency.sol:
|
||||||
|
|
|
@ -5,6 +5,7 @@ import 'package:cake_wallet/core/validator.dart';
|
||||||
import 'package:cake_wallet/entities/mnemonic_item.dart';
|
import 'package:cake_wallet/entities/mnemonic_item.dart';
|
||||||
import 'package:cw_core/wallet_type.dart';
|
import 'package:cw_core/wallet_type.dart';
|
||||||
import 'package:cake_wallet/monero/monero.dart';
|
import 'package:cake_wallet/monero/monero.dart';
|
||||||
|
import 'package:cake_wallet/nano/nano.dart';
|
||||||
import 'package:cake_wallet/utils/language_list.dart';
|
import 'package:cake_wallet/utils/language_list.dart';
|
||||||
|
|
||||||
class SeedValidator extends Validator<MnemonicItem> {
|
class SeedValidator extends Validator<MnemonicItem> {
|
||||||
|
@ -28,6 +29,9 @@ class SeedValidator extends Validator<MnemonicItem> {
|
||||||
return haven!.getMoneroWordList(language);
|
return haven!.getMoneroWordList(language);
|
||||||
case WalletType.ethereum:
|
case WalletType.ethereum:
|
||||||
return ethereum!.getEthereumWordList(language);
|
return ethereum!.getEthereumWordList(language);
|
||||||
|
case WalletType.nano:
|
||||||
|
case WalletType.banano:
|
||||||
|
return nano!.getNanoWordList(language);
|
||||||
default:
|
default:
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,15 +39,11 @@ class WalletCreationService {
|
||||||
|
|
||||||
bool exists(String name) {
|
bool exists(String name) {
|
||||||
final walletName = name.toLowerCase();
|
final walletName = name.toLowerCase();
|
||||||
return walletInfoSource
|
return walletInfoSource.values.any((walletInfo) => walletInfo.name.toLowerCase() == walletName);
|
||||||
.values
|
|
||||||
.any((walletInfo) => walletInfo.name.toLowerCase() == walletName);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool typeExists(WalletType type) {
|
bool typeExists(WalletType type) {
|
||||||
return walletInfoSource
|
return walletInfoSource.values.any((walletInfo) => walletInfo.type == type);
|
||||||
.values
|
|
||||||
.any((walletInfo) => walletInfo.type == type);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void checkIfExists(String name) {
|
void checkIfExists(String name) {
|
||||||
|
@ -60,15 +56,12 @@ class WalletCreationService {
|
||||||
checkIfExists(credentials.name);
|
checkIfExists(credentials.name);
|
||||||
final password = generateWalletPassword();
|
final password = generateWalletPassword();
|
||||||
credentials.password = password;
|
credentials.password = password;
|
||||||
await keyService.saveWalletPassword(
|
await keyService.saveWalletPassword(password: password, walletName: credentials.name);
|
||||||
password: password, walletName: credentials.name);
|
final wallet = await _service!.create(credentials);
|
||||||
final wallet = await _service!.create(credentials);
|
|
||||||
|
|
||||||
if (wallet.type == WalletType.monero) {
|
if (wallet.type == WalletType.monero) {
|
||||||
await sharedPreferences
|
await sharedPreferences.setBool(
|
||||||
.setBool(
|
PreferencesKey.moneroWalletUpdateV1Key(wallet.name), _isNewMoneroWalletPasswordUpdated);
|
||||||
PreferencesKey.moneroWalletUpdateV1Key(wallet.name),
|
|
||||||
_isNewMoneroWalletPasswordUpdated);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return wallet;
|
return wallet;
|
||||||
|
@ -78,15 +71,12 @@ class WalletCreationService {
|
||||||
checkIfExists(credentials.name);
|
checkIfExists(credentials.name);
|
||||||
final password = generateWalletPassword();
|
final password = generateWalletPassword();
|
||||||
credentials.password = password;
|
credentials.password = password;
|
||||||
await keyService.saveWalletPassword(
|
await keyService.saveWalletPassword(password: password, walletName: credentials.name);
|
||||||
password: password, walletName: credentials.name);
|
|
||||||
final wallet = await _service!.restoreFromKeys(credentials);
|
final wallet = await _service!.restoreFromKeys(credentials);
|
||||||
|
|
||||||
if (wallet.type == WalletType.monero) {
|
if (wallet.type == WalletType.monero) {
|
||||||
await sharedPreferences
|
await sharedPreferences.setBool(
|
||||||
.setBool(
|
PreferencesKey.moneroWalletUpdateV1Key(wallet.name), _isNewMoneroWalletPasswordUpdated);
|
||||||
PreferencesKey.moneroWalletUpdateV1Key(wallet.name),
|
|
||||||
_isNewMoneroWalletPasswordUpdated);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return wallet;
|
return wallet;
|
||||||
|
@ -96,15 +86,12 @@ class WalletCreationService {
|
||||||
checkIfExists(credentials.name);
|
checkIfExists(credentials.name);
|
||||||
final password = generateWalletPassword();
|
final password = generateWalletPassword();
|
||||||
credentials.password = password;
|
credentials.password = password;
|
||||||
await keyService.saveWalletPassword(
|
await keyService.saveWalletPassword(password: password, walletName: credentials.name);
|
||||||
password: password, walletName: credentials.name);
|
|
||||||
final wallet = await _service!.restoreFromSeed(credentials);
|
final wallet = await _service!.restoreFromSeed(credentials);
|
||||||
|
|
||||||
if (wallet.type == WalletType.monero) {
|
if (wallet.type == WalletType.monero) {
|
||||||
await sharedPreferences
|
await sharedPreferences.setBool(
|
||||||
.setBool(
|
PreferencesKey.moneroWalletUpdateV1Key(wallet.name), _isNewMoneroWalletPasswordUpdated);
|
||||||
PreferencesKey.moneroWalletUpdateV1Key(wallet.name),
|
|
||||||
_isNewMoneroWalletPasswordUpdated);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return wallet;
|
return wallet;
|
||||||
|
|
90
lib/di.dart
90
lib/di.dart
|
@ -13,6 +13,7 @@ import 'package:cake_wallet/entities/exchange_api_mode.dart';
|
||||||
import 'package:cake_wallet/entities/parse_address_from_domain.dart';
|
import 'package:cake_wallet/entities/parse_address_from_domain.dart';
|
||||||
import 'package:cake_wallet/entities/receive_page_option.dart';
|
import 'package:cake_wallet/entities/receive_page_option.dart';
|
||||||
import 'package:cake_wallet/ethereum/ethereum.dart';
|
import 'package:cake_wallet/ethereum/ethereum.dart';
|
||||||
|
import 'package:cake_wallet/nano/nano.dart';
|
||||||
import 'package:cake_wallet/ionia/ionia_anypay.dart';
|
import 'package:cake_wallet/ionia/ionia_anypay.dart';
|
||||||
import 'package:cake_wallet/ionia/ionia_gift_card.dart';
|
import 'package:cake_wallet/ionia/ionia_gift_card.dart';
|
||||||
import 'package:cake_wallet/ionia/ionia_tip.dart';
|
import 'package:cake_wallet/ionia/ionia_tip.dart';
|
||||||
|
@ -26,8 +27,13 @@ import 'package:cake_wallet/src/screens/dashboard/desktop_widgets/desktop_wallet
|
||||||
import 'package:cake_wallet/src/screens/dashboard/edit_token_page.dart';
|
import 'package:cake_wallet/src/screens/dashboard/edit_token_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/dashboard/home_settings_page.dart';
|
import 'package:cake_wallet/src/screens/dashboard/home_settings_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/dashboard/widgets/transactions_page.dart';
|
import 'package:cake_wallet/src/screens/dashboard/widgets/transactions_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/nano/nano_change_rep_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/nano_accounts/nano_account_edit_or_create_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/nano_accounts/nano_account_list_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/nodes/pow_node_create_or_edit_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/receive/anonpay_invoice_page.dart';
|
import 'package:cake_wallet/src/screens/receive/anonpay_invoice_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/receive/anonpay_receive_page.dart';
|
import 'package:cake_wallet/src/screens/receive/anonpay_receive_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/restore/wallet_restore_choose_derivation.dart';
|
||||||
import 'package:cake_wallet/src/screens/settings/display_settings_page.dart';
|
import 'package:cake_wallet/src/screens/settings/display_settings_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/settings/manage_nodes_page.dart';
|
import 'package:cake_wallet/src/screens/settings/manage_nodes_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/settings/other_settings_page.dart';
|
import 'package:cake_wallet/src/screens/settings/other_settings_page.dart';
|
||||||
|
@ -73,6 +79,9 @@ import 'package:cake_wallet/src/screens/dashboard/widgets/balance_page.dart';
|
||||||
import 'package:cake_wallet/view_model/ionia/ionia_account_view_model.dart';
|
import 'package:cake_wallet/view_model/ionia/ionia_account_view_model.dart';
|
||||||
import 'package:cake_wallet/view_model/ionia/ionia_gift_cards_list_view_model.dart';
|
import 'package:cake_wallet/view_model/ionia/ionia_gift_cards_list_view_model.dart';
|
||||||
import 'package:cake_wallet/view_model/ionia/ionia_purchase_merch_view_model.dart';
|
import 'package:cake_wallet/view_model/ionia/ionia_purchase_merch_view_model.dart';
|
||||||
|
import 'package:cake_wallet/view_model/nano_account_list/nano_account_edit_or_create_view_model.dart';
|
||||||
|
import 'package:cake_wallet/view_model/nano_account_list/nano_account_list_view_model.dart';
|
||||||
|
import 'package:cake_wallet/view_model/node_list/pow_node_list_view_model.dart';
|
||||||
import 'package:cake_wallet/view_model/set_up_2fa_viewmodel.dart';
|
import 'package:cake_wallet/view_model/set_up_2fa_viewmodel.dart';
|
||||||
import 'package:cake_wallet/view_model/restore/restore_from_qr_vm.dart';
|
import 'package:cake_wallet/view_model/restore/restore_from_qr_vm.dart';
|
||||||
import 'package:cake_wallet/view_model/settings/display_settings_view_model.dart';
|
import 'package:cake_wallet/view_model/settings/display_settings_view_model.dart';
|
||||||
|
@ -83,7 +92,9 @@ import 'package:cake_wallet/view_model/advanced_privacy_settings_view_model.dart
|
||||||
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_item.dart';
|
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_item.dart';
|
||||||
import 'package:cake_wallet/view_model/wallet_list/wallet_edit_view_model.dart';
|
import 'package:cake_wallet/view_model/wallet_list/wallet_edit_view_model.dart';
|
||||||
import 'package:cake_wallet/view_model/wallet_list/wallet_list_item.dart';
|
import 'package:cake_wallet/view_model/wallet_list/wallet_list_item.dart';
|
||||||
|
import 'package:cake_wallet/view_model/wallet_restore_choose_derivation_view_model.dart';
|
||||||
import 'package:cw_core/erc20_token.dart';
|
import 'package:cw_core/erc20_token.dart';
|
||||||
|
import 'package:cw_core/nano_account.dart';
|
||||||
import 'package:cw_core/unspent_coins_info.dart';
|
import 'package:cw_core/unspent_coins_info.dart';
|
||||||
import 'package:cake_wallet/core/backup_service.dart';
|
import 'package:cake_wallet/core/backup_service.dart';
|
||||||
import 'package:cw_core/wallet_service.dart';
|
import 'package:cw_core/wallet_service.dart';
|
||||||
|
@ -206,6 +217,7 @@ import 'package:cake_wallet/src/screens/receive/fullscreen_qr_page.dart';
|
||||||
import 'package:cake_wallet/core/wallet_loading_service.dart';
|
import 'package:cake_wallet/core/wallet_loading_service.dart';
|
||||||
import 'package:cw_core/crypto_currency.dart';
|
import 'package:cw_core/crypto_currency.dart';
|
||||||
import 'package:cake_wallet/entities/qr_view_data.dart';
|
import 'package:cake_wallet/entities/qr_view_data.dart';
|
||||||
|
import 'package:cake_wallet/nano/nano.dart' as nanoNano;
|
||||||
|
|
||||||
import 'core/totp_request_details.dart';
|
import 'core/totp_request_details.dart';
|
||||||
|
|
||||||
|
@ -214,6 +226,7 @@ final getIt = GetIt.instance;
|
||||||
var _isSetupFinished = false;
|
var _isSetupFinished = false;
|
||||||
late Box<WalletInfo> _walletInfoSource;
|
late Box<WalletInfo> _walletInfoSource;
|
||||||
late Box<Node> _nodeSource;
|
late Box<Node> _nodeSource;
|
||||||
|
late Box<Node> _powNodeSource;
|
||||||
late Box<Contact> _contactSource;
|
late Box<Contact> _contactSource;
|
||||||
late Box<Trade> _tradesSource;
|
late Box<Trade> _tradesSource;
|
||||||
late Box<Template> _templates;
|
late Box<Template> _templates;
|
||||||
|
@ -226,6 +239,7 @@ late Box<AnonpayInvoiceInfo> _anonpayInvoiceInfoSource;
|
||||||
Future<void> setup({
|
Future<void> setup({
|
||||||
required Box<WalletInfo> walletInfoSource,
|
required Box<WalletInfo> walletInfoSource,
|
||||||
required Box<Node> nodeSource,
|
required Box<Node> nodeSource,
|
||||||
|
required Box<Node> powNodeSource,
|
||||||
required Box<Contact> contactSource,
|
required Box<Contact> contactSource,
|
||||||
required Box<Trade> tradesSource,
|
required Box<Trade> tradesSource,
|
||||||
required Box<Template> templates,
|
required Box<Template> templates,
|
||||||
|
@ -237,6 +251,7 @@ Future<void> setup({
|
||||||
}) async {
|
}) async {
|
||||||
_walletInfoSource = walletInfoSource;
|
_walletInfoSource = walletInfoSource;
|
||||||
_nodeSource = nodeSource;
|
_nodeSource = nodeSource;
|
||||||
|
_powNodeSource = powNodeSource;
|
||||||
_contactSource = contactSource;
|
_contactSource = contactSource;
|
||||||
_tradesSource = tradesSource;
|
_tradesSource = tradesSource;
|
||||||
_templates = templates;
|
_templates = templates;
|
||||||
|
@ -259,6 +274,7 @@ Future<void> setup({
|
||||||
|
|
||||||
final settingsStore = await SettingsStoreBase.load(
|
final settingsStore = await SettingsStoreBase.load(
|
||||||
nodeSource: _nodeSource,
|
nodeSource: _nodeSource,
|
||||||
|
powNodeSource: _powNodeSource,
|
||||||
isBitcoinBuyEnabled: isBitcoinBuyEnabled,
|
isBitcoinBuyEnabled: isBitcoinBuyEnabled,
|
||||||
// Enforce darkTheme on platforms other than mobile till the design for other themes is completed
|
// Enforce darkTheme on platforms other than mobile till the design for other themes is completed
|
||||||
initialTheme: ResponsiveLayoutUtil.instance.isMobile && DeviceInfo.instance.isMobile
|
initialTheme: ResponsiveLayoutUtil.instance.isMobile && DeviceInfo.instance.isMobile
|
||||||
|
@ -271,6 +287,7 @@ Future<void> setup({
|
||||||
}
|
}
|
||||||
|
|
||||||
getIt.registerFactory<Box<Node>>(() => _nodeSource);
|
getIt.registerFactory<Box<Node>>(() => _nodeSource);
|
||||||
|
getIt.registerFactory<Box<Node>>(() => _powNodeSource, instanceName: Node.boxName + "pow");
|
||||||
|
|
||||||
getIt.registerSingleton<FlutterSecureStorage>(FlutterSecureStorage());
|
getIt.registerSingleton<FlutterSecureStorage>(FlutterSecureStorage());
|
||||||
getIt.registerSingleton(AuthenticationStore());
|
getIt.registerSingleton(AuthenticationStore());
|
||||||
|
@ -612,20 +629,30 @@ Future<void> setup({
|
||||||
editingWallet: editingWallet);
|
editingWallet: editingWallet);
|
||||||
});
|
});
|
||||||
|
|
||||||
getIt.registerFactory(() {
|
getIt.registerFactory<NanoAccountListViewModel>(() {
|
||||||
final wallet = getIt.get<AppStore>().wallet!;
|
final wallet = getIt.get<AppStore>().wallet!;
|
||||||
|
if (wallet.type == WalletType.nano || wallet.type == WalletType.banano) {
|
||||||
|
return NanoAccountListViewModel(wallet);
|
||||||
|
}
|
||||||
|
throw Exception(
|
||||||
|
'Unexpected wallet type: ${wallet.type} for generate Nano/Banano AccountListViewModel');
|
||||||
|
});
|
||||||
|
|
||||||
|
getIt.registerFactory<MoneroAccountListViewModel>(() {
|
||||||
|
final wallet = getIt.get<AppStore>().wallet!;
|
||||||
if (wallet.type == WalletType.monero || wallet.type == WalletType.haven) {
|
if (wallet.type == WalletType.monero || wallet.type == WalletType.haven) {
|
||||||
return MoneroAccountListViewModel(wallet);
|
return MoneroAccountListViewModel(wallet);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw Exception(
|
throw Exception(
|
||||||
'Unexpected wallet type: ${wallet.type} for generate MoneroAccountListViewModel');
|
'Unexpected wallet type: ${wallet.type} for generate Nano/Monero AccountListViewModel');
|
||||||
});
|
});
|
||||||
|
|
||||||
getIt.registerFactory(
|
getIt.registerFactory(
|
||||||
() => MoneroAccountListPage(accountListViewModel: getIt.get<MoneroAccountListViewModel>()));
|
() => MoneroAccountListPage(accountListViewModel: getIt.get<MoneroAccountListViewModel>()));
|
||||||
|
|
||||||
|
getIt.registerFactory(
|
||||||
|
() => NanoAccountListPage(accountListViewModel: getIt.get<NanoAccountListViewModel>()));
|
||||||
|
|
||||||
/*getIt.registerFactory(() {
|
/*getIt.registerFactory(() {
|
||||||
final wallet = getIt.get<AppStore>().wallet;
|
final wallet = getIt.get<AppStore>().wallet;
|
||||||
|
|
||||||
|
@ -653,6 +680,18 @@ Future<void> setup({
|
||||||
moneroAccountCreationViewModel:
|
moneroAccountCreationViewModel:
|
||||||
getIt.get<MoneroAccountEditOrCreateViewModel>(param1: account)));
|
getIt.get<MoneroAccountEditOrCreateViewModel>(param1: account)));
|
||||||
|
|
||||||
|
getIt.registerFactoryParam<NanoAccountEditOrCreateViewModel, NanoAccount?, void>(
|
||||||
|
(NanoAccount? account, _) =>
|
||||||
|
NanoAccountEditOrCreateViewModel(nano!.getAccountList(getIt.get<AppStore>().wallet!),
|
||||||
|
// banano?.getAccountList(getIt.get<AppStore>().wallet!),
|
||||||
|
wallet: getIt.get<AppStore>().wallet!,
|
||||||
|
accountListItem: account));
|
||||||
|
|
||||||
|
getIt.registerFactoryParam<NanoAccountEditOrCreatePage, NanoAccount?, void>(
|
||||||
|
(NanoAccount? account, _) => NanoAccountEditOrCreatePage(
|
||||||
|
nanoAccountCreationViewModel:
|
||||||
|
getIt.get<NanoAccountEditOrCreateViewModel>(param1: account)));
|
||||||
|
|
||||||
getIt.registerFactory(() {
|
getIt.registerFactory(() {
|
||||||
return DisplaySettingsViewModel(getIt.get<SettingsStore>());
|
return DisplaySettingsViewModel(getIt.get<SettingsStore>());
|
||||||
});
|
});
|
||||||
|
@ -696,6 +735,11 @@ Future<void> setup({
|
||||||
return NodeListViewModel(_nodeSource, appStore);
|
return NodeListViewModel(_nodeSource, appStore);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
getIt.registerFactory(() {
|
||||||
|
final appStore = getIt.get<AppStore>();
|
||||||
|
return PowNodeListViewModel(_powNodeSource, appStore);
|
||||||
|
});
|
||||||
|
|
||||||
getIt.registerFactory(
|
getIt.registerFactory(
|
||||||
() => ConnectionSyncPage(getIt.get<DashboardViewModel>(), getIt.get<Web3WalletService>()),
|
() => ConnectionSyncPage(getIt.get<DashboardViewModel>(), getIt.get<Web3WalletService>()),
|
||||||
);
|
);
|
||||||
|
@ -709,13 +753,23 @@ Future<void> setup({
|
||||||
|
|
||||||
getIt.registerFactory(() => OtherSettingsPage(getIt.get<OtherSettingsViewModel>()));
|
getIt.registerFactory(() => OtherSettingsPage(getIt.get<OtherSettingsViewModel>()));
|
||||||
|
|
||||||
getIt.registerFactoryParam<NodeCreateOrEditViewModel, WalletType?, void>((WalletType? type, _) =>
|
getIt.registerFactory(() => NanoChangeRepPage(getIt.get<AppStore>().wallet!));
|
||||||
NodeCreateOrEditViewModel(
|
|
||||||
_nodeSource, type ?? getIt.get<AppStore>().wallet!.type, getIt.get<SettingsStore>()));
|
getIt.registerFactoryParam<NodeCreateOrEditViewModel, WalletType?, bool?>(
|
||||||
|
(WalletType? type, bool? isPow) => NodeCreateOrEditViewModel(
|
||||||
|
(isPow ?? false) ? _powNodeSource : _nodeSource,
|
||||||
|
type ?? getIt.get<AppStore>().wallet!.type,
|
||||||
|
getIt.get<SettingsStore>()));
|
||||||
|
|
||||||
getIt.registerFactoryParam<NodeCreateOrEditPage, Node?, bool?>(
|
getIt.registerFactoryParam<NodeCreateOrEditPage, Node?, bool?>(
|
||||||
(Node? editingNode, bool? isSelected) => NodeCreateOrEditPage(
|
(Node? editingNode, bool? isSelected) => NodeCreateOrEditPage(
|
||||||
nodeCreateOrEditViewModel: getIt.get<NodeCreateOrEditViewModel>(),
|
nodeCreateOrEditViewModel: getIt.get<NodeCreateOrEditViewModel>(param2: false),
|
||||||
|
editingNode: editingNode,
|
||||||
|
isSelected: isSelected));
|
||||||
|
|
||||||
|
getIt.registerFactoryParam<PowNodeCreateOrEditPage, Node?, bool?>(
|
||||||
|
(Node? editingNode, bool? isSelected) => PowNodeCreateOrEditPage(
|
||||||
|
nodeCreateOrEditViewModel: getIt.get<NodeCreateOrEditViewModel>(param2: true),
|
||||||
editingNode: editingNode,
|
editingNode: editingNode,
|
||||||
isSelected: isSelected));
|
isSelected: isSelected));
|
||||||
|
|
||||||
|
@ -771,6 +825,8 @@ Future<void> setup({
|
||||||
return bitcoin!.createLitecoinWalletService(_walletInfoSource, _unspentCoinsInfoSource);
|
return bitcoin!.createLitecoinWalletService(_walletInfoSource, _unspentCoinsInfoSource);
|
||||||
case WalletType.ethereum:
|
case WalletType.ethereum:
|
||||||
return ethereum!.createEthereumWalletService(_walletInfoSource);
|
return ethereum!.createEthereumWalletService(_walletInfoSource);
|
||||||
|
case WalletType.nano:
|
||||||
|
return nano!.createNanoWalletService(_walletInfoSource);
|
||||||
default:
|
default:
|
||||||
throw Exception('Unexpected token: ${param1.toString()} for generating of WalletService');
|
throw Exception('Unexpected token: ${param1.toString()} for generating of WalletService');
|
||||||
}
|
}
|
||||||
|
@ -798,6 +854,15 @@ Future<void> setup({
|
||||||
getIt.registerFactoryParam<WalletRestorePage, WalletType, void>(
|
getIt.registerFactoryParam<WalletRestorePage, WalletType, void>(
|
||||||
(type, _) => WalletRestorePage(getIt.get<WalletRestoreViewModel>(param1: type)));
|
(type, _) => WalletRestorePage(getIt.get<WalletRestoreViewModel>(param1: type)));
|
||||||
|
|
||||||
|
getIt.registerFactoryParam<WalletRestoreChooseDerivationViewModel, List<DerivationInfo>, void>(
|
||||||
|
(derivations, _) => WalletRestoreChooseDerivationViewModel(derivationInfos: derivations));
|
||||||
|
|
||||||
|
getIt.registerFactoryParam<WalletRestoreChooseDerivationPage, List<DerivationInfo>, void>(
|
||||||
|
(credentials, _) =>
|
||||||
|
WalletRestoreChooseDerivationPage(getIt.get<WalletRestoreChooseDerivationViewModel>(
|
||||||
|
param1: credentials,
|
||||||
|
)));
|
||||||
|
|
||||||
getIt.registerFactoryParam<TransactionDetailsViewModel, TransactionInfo, void>(
|
getIt.registerFactoryParam<TransactionDetailsViewModel, TransactionInfo, void>(
|
||||||
(TransactionInfo transactionInfo, _) {
|
(TransactionInfo transactionInfo, _) {
|
||||||
final wallet = getIt.get<AppStore>().wallet!;
|
final wallet = getIt.get<AppStore>().wallet!;
|
||||||
|
@ -911,8 +976,8 @@ Future<void> setup({
|
||||||
|
|
||||||
getIt.registerFactory(() => YatService());
|
getIt.registerFactory(() => YatService());
|
||||||
|
|
||||||
getIt.registerFactory(() => AddressResolver(
|
getIt.registerFactory(() =>
|
||||||
yatService: getIt.get<YatService>(), wallet: getIt.get<AppStore>().wallet!));
|
AddressResolver(yatService: getIt.get<YatService>(), wallet: getIt.get<AppStore>().wallet!));
|
||||||
|
|
||||||
getIt.registerFactoryParam<FullscreenQRPage, QrViewData, void>(
|
getIt.registerFactoryParam<FullscreenQRPage, QrViewData, void>(
|
||||||
(QrViewData viewData, _) => FullscreenQRPage(qrViewData: viewData));
|
(QrViewData viewData, _) => FullscreenQRPage(qrViewData: viewData));
|
||||||
|
@ -1077,7 +1142,12 @@ Future<void> setup({
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
getIt.registerFactory<ManageNodesPage>(() => ManageNodesPage(getIt.get<NodeListViewModel>()));
|
getIt.registerFactoryParam<ManageNodesPage, bool, void>((bool isPow, _) {
|
||||||
|
if (isPow) {
|
||||||
|
return ManageNodesPage(isPow, powNodeListViewModel: getIt.get<PowNodeListViewModel>());
|
||||||
|
}
|
||||||
|
return ManageNodesPage(isPow, nodeListViewModel: getIt.get<NodeListViewModel>());
|
||||||
|
});
|
||||||
|
|
||||||
_isSetupFinished = true;
|
_isSetupFinished = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,12 +26,15 @@ const cakeWalletBitcoinElectrumUri = 'electrum.cakewallet.com:50002';
|
||||||
const cakeWalletLitecoinElectrumUri = 'ltc-electrum.cakewallet.com:50002';
|
const cakeWalletLitecoinElectrumUri = 'ltc-electrum.cakewallet.com:50002';
|
||||||
const havenDefaultNodeUri = 'nodes.havenprotocol.org:443';
|
const havenDefaultNodeUri = 'nodes.havenprotocol.org:443';
|
||||||
const ethereumDefaultNodeUri = 'ethereum.publicnode.com';
|
const ethereumDefaultNodeUri = 'ethereum.publicnode.com';
|
||||||
|
const nanoDefaultNodeUri = 'rpc.nano.to';
|
||||||
|
const nanoDefaultPowNodeUri = 'rpc.nano.to';
|
||||||
|
|
||||||
Future<void> defaultSettingsMigration(
|
Future<void> defaultSettingsMigration(
|
||||||
{required int version,
|
{required int version,
|
||||||
required SharedPreferences sharedPreferences,
|
required SharedPreferences sharedPreferences,
|
||||||
required FlutterSecureStorage secureStorage,
|
required FlutterSecureStorage secureStorage,
|
||||||
required Box<Node> nodes,
|
required Box<Node> nodes,
|
||||||
|
required Box<Node> powNodes,
|
||||||
required Box<WalletInfo> walletInfoSource,
|
required Box<WalletInfo> walletInfoSource,
|
||||||
required Box<Trade> tradeSource,
|
required Box<Trade> tradeSource,
|
||||||
required Box<Contact> contactSource}) async {
|
required Box<Contact> contactSource}) async {
|
||||||
|
@ -40,41 +43,36 @@ Future<void> defaultSettingsMigration(
|
||||||
}
|
}
|
||||||
|
|
||||||
// check current nodes for nullability regardless of the version
|
// check current nodes for nullability regardless of the version
|
||||||
await checkCurrentNodes(nodes, sharedPreferences);
|
await checkCurrentNodes(nodes, powNodes, sharedPreferences);
|
||||||
|
|
||||||
|
final isNewInstall =
|
||||||
|
sharedPreferences.getInt(PreferencesKey.currentDefaultSettingsMigrationVersion) == null;
|
||||||
|
|
||||||
await _validateWalletInfoBoxData(walletInfoSource);
|
await _validateWalletInfoBoxData(walletInfoSource);
|
||||||
|
|
||||||
final isNewInstall = sharedPreferences
|
await sharedPreferences.setBool(PreferencesKey.isNewInstall, isNewInstall);
|
||||||
.getInt(PreferencesKey.currentDefaultSettingsMigrationVersion) == null;
|
|
||||||
|
|
||||||
await sharedPreferences.setBool(
|
final currentVersion =
|
||||||
PreferencesKey.isNewInstall, isNewInstall);
|
sharedPreferences.getInt(PreferencesKey.currentDefaultSettingsMigrationVersion) ?? 0;
|
||||||
|
|
||||||
|
|
||||||
final currentVersion = sharedPreferences
|
|
||||||
.getInt(PreferencesKey.currentDefaultSettingsMigrationVersion) ??
|
|
||||||
0;
|
|
||||||
if (currentVersion >= version) {
|
if (currentVersion >= version) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final migrationVersionsLength = version - currentVersion;
|
final migrationVersionsLength = version - currentVersion;
|
||||||
final migrationVersions = List<int>.generate(
|
final migrationVersions =
|
||||||
migrationVersionsLength, (i) => currentVersion + (i + 1));
|
List<int>.generate(migrationVersionsLength, (i) => currentVersion + (i + 1));
|
||||||
|
|
||||||
await Future.forEach(migrationVersions, (int version) async {
|
await Future.forEach(migrationVersions, (int version) async {
|
||||||
try {
|
try {
|
||||||
switch (version) {
|
switch (version) {
|
||||||
case 1:
|
case 1:
|
||||||
await sharedPreferences.setString(
|
await sharedPreferences.setString(
|
||||||
PreferencesKey.currentFiatCurrencyKey,
|
PreferencesKey.currentFiatCurrencyKey, FiatCurrency.usd.toString());
|
||||||
FiatCurrency.usd.toString());
|
await sharedPreferences.setInt(PreferencesKey.currentTransactionPriorityKeyLegacy,
|
||||||
await sharedPreferences.setInt(
|
|
||||||
PreferencesKey.currentTransactionPriorityKeyLegacy,
|
|
||||||
monero!.getDefaultTransactionPriority().raw);
|
monero!.getDefaultTransactionPriority().raw);
|
||||||
await sharedPreferences.setInt(
|
await sharedPreferences.setInt(
|
||||||
PreferencesKey.currentBalanceDisplayModeKey,
|
PreferencesKey.currentBalanceDisplayModeKey, BalanceDisplayMode.availableBalance.raw);
|
||||||
BalanceDisplayMode.availableBalance.raw);
|
|
||||||
await sharedPreferences.setBool('save_recipient_address', true);
|
await sharedPreferences.setBool('save_recipient_address', true);
|
||||||
await resetToDefault(nodes);
|
await resetToDefault(nodes);
|
||||||
await changeMoneroCurrentNodeToDefault(
|
await changeMoneroCurrentNodeToDefault(
|
||||||
|
@ -83,14 +81,12 @@ Future<void> defaultSettingsMigration(
|
||||||
sharedPreferences: sharedPreferences, nodes: nodes);
|
sharedPreferences: sharedPreferences, nodes: nodes);
|
||||||
await changeLitecoinCurrentElectrumServerToDefault(
|
await changeLitecoinCurrentElectrumServerToDefault(
|
||||||
sharedPreferences: sharedPreferences, nodes: nodes);
|
sharedPreferences: sharedPreferences, nodes: nodes);
|
||||||
await changeHavenCurrentNodeToDefault(
|
await changeHavenCurrentNodeToDefault(sharedPreferences: sharedPreferences, nodes: nodes);
|
||||||
sharedPreferences: sharedPreferences, nodes: nodes);
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
await replaceNodesMigration(nodes: nodes);
|
await replaceNodesMigration(nodes: nodes);
|
||||||
await replaceDefaultNode(
|
await replaceDefaultNode(sharedPreferences: sharedPreferences, nodes: nodes);
|
||||||
sharedPreferences: sharedPreferences, nodes: nodes);
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
|
@ -124,7 +120,7 @@ Future<void> defaultSettingsMigration(
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 12:
|
case 12:
|
||||||
await checkCurrentNodes(nodes, sharedPreferences);
|
await checkCurrentNodes(nodes, powNodes, sharedPreferences);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 13:
|
case 13:
|
||||||
|
@ -135,14 +131,13 @@ Future<void> defaultSettingsMigration(
|
||||||
await addLitecoinElectrumServerList(nodes: nodes);
|
await addLitecoinElectrumServerList(nodes: nodes);
|
||||||
await changeLitecoinCurrentElectrumServerToDefault(
|
await changeLitecoinCurrentElectrumServerToDefault(
|
||||||
sharedPreferences: sharedPreferences, nodes: nodes);
|
sharedPreferences: sharedPreferences, nodes: nodes);
|
||||||
await checkCurrentNodes(nodes, sharedPreferences);
|
await checkCurrentNodes(nodes, powNodes, sharedPreferences);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 16:
|
case 16:
|
||||||
await addHavenNodeList(nodes: nodes);
|
await addHavenNodeList(nodes: nodes);
|
||||||
await changeHavenCurrentNodeToDefault(
|
await changeHavenCurrentNodeToDefault(sharedPreferences: sharedPreferences, nodes: nodes);
|
||||||
sharedPreferences: sharedPreferences, nodes: nodes);
|
await checkCurrentNodes(nodes, powNodes, sharedPreferences);
|
||||||
await checkCurrentNodes(nodes, sharedPreferences);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 17:
|
case 17:
|
||||||
|
@ -164,6 +159,13 @@ Future<void> defaultSettingsMigration(
|
||||||
await changeEthereumCurrentNodeToDefault(
|
await changeEthereumCurrentNodeToDefault(
|
||||||
sharedPreferences: sharedPreferences, nodes: nodes);
|
sharedPreferences: sharedPreferences, nodes: nodes);
|
||||||
break;
|
break;
|
||||||
|
case 22:
|
||||||
|
await addNanoNodeList(nodes: nodes);
|
||||||
|
await addNanoPowNodeList(nodes: nodes);
|
||||||
|
await changeNanoCurrentNodeToDefault(sharedPreferences: sharedPreferences, nodes: nodes);
|
||||||
|
await changeNanoCurrentPowNodeToDefault(
|
||||||
|
sharedPreferences: sharedPreferences, nodes: powNodes);
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
@ -176,8 +178,7 @@ Future<void> defaultSettingsMigration(
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
await sharedPreferences.setInt(
|
await sharedPreferences.setInt(PreferencesKey.currentDefaultSettingsMigrationVersion, version);
|
||||||
PreferencesKey.currentDefaultSettingsMigrationVersion, version);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _validateWalletInfoBoxData(Box<WalletInfo> walletInfoSource) async {
|
Future<void> _validateWalletInfoBoxData(Box<WalletInfo> walletInfoSource) async {
|
||||||
|
@ -247,8 +248,8 @@ Future<void> validateBitcoinSavedTransactionPriority(SharedPreferences sharedPre
|
||||||
final int? savedBitcoinPriority =
|
final int? savedBitcoinPriority =
|
||||||
sharedPreferences.getInt(PreferencesKey.bitcoinTransactionPriority);
|
sharedPreferences.getInt(PreferencesKey.bitcoinTransactionPriority);
|
||||||
if (!bitcoin!.getTransactionPriorities().any((element) => element.raw == savedBitcoinPriority)) {
|
if (!bitcoin!.getTransactionPriorities().any((element) => element.raw == savedBitcoinPriority)) {
|
||||||
await sharedPreferences.setInt(
|
await sharedPreferences.setInt(PreferencesKey.bitcoinTransactionPriority,
|
||||||
PreferencesKey.bitcoinTransactionPriority, bitcoin!.getMediumTransactionPriority().serialize());
|
bitcoin!.getMediumTransactionPriority().serialize());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -265,10 +266,9 @@ Future<void> replaceNodesMigration({required Box<Node> nodes}) async {
|
||||||
final replaceNodes = <String, Node>{
|
final replaceNodes = <String, Node>{
|
||||||
'eu-node.cakewallet.io:18081':
|
'eu-node.cakewallet.io:18081':
|
||||||
Node(uri: 'xmr-node-eu.cakewallet.com:18081', type: WalletType.monero),
|
Node(uri: 'xmr-node-eu.cakewallet.com:18081', type: WalletType.monero),
|
||||||
'node.cakewallet.io:18081': Node(
|
'node.cakewallet.io:18081':
|
||||||
uri: 'xmr-node-usa-east.cakewallet.com:18081', type: WalletType.monero),
|
Node(uri: 'xmr-node-usa-east.cakewallet.com:18081', type: WalletType.monero),
|
||||||
'node.xmr.ru:13666':
|
'node.xmr.ru:13666': Node(uri: 'node.monero.net:18081', type: WalletType.monero)
|
||||||
Node(uri: 'node.monero.net:18081', type: WalletType.monero)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
nodes.values.forEach((Node node) async {
|
nodes.values.forEach((Node node) async {
|
||||||
|
@ -284,8 +284,7 @@ Future<void> replaceNodesMigration({required Box<Node> nodes}) async {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> changeMoneroCurrentNodeToDefault(
|
Future<void> changeMoneroCurrentNodeToDefault(
|
||||||
{required SharedPreferences sharedPreferences,
|
{required SharedPreferences sharedPreferences, required Box<Node> nodes}) async {
|
||||||
required Box<Node> nodes}) async {
|
|
||||||
final node = getMoneroDefaultNode(nodes: nodes);
|
final node = getMoneroDefaultNode(nodes: nodes);
|
||||||
final nodeId = node.key as int? ?? 0; // 0 - England
|
final nodeId = node.key as int? ?? 0; // 0 - England
|
||||||
|
|
||||||
|
@ -293,27 +292,35 @@ Future<void> changeMoneroCurrentNodeToDefault(
|
||||||
}
|
}
|
||||||
|
|
||||||
Node? getBitcoinDefaultElectrumServer({required Box<Node> nodes}) {
|
Node? getBitcoinDefaultElectrumServer({required Box<Node> nodes}) {
|
||||||
return nodes.values.firstWhereOrNull(
|
return nodes.values
|
||||||
(Node node) => node.uriRaw == cakeWalletBitcoinElectrumUri)
|
.firstWhereOrNull((Node node) => node.uriRaw == cakeWalletBitcoinElectrumUri) ??
|
||||||
?? nodes.values.firstWhereOrNull((node) => node.type == WalletType.bitcoin);
|
nodes.values.firstWhereOrNull((node) => node.type == WalletType.bitcoin);
|
||||||
}
|
}
|
||||||
|
|
||||||
Node? getLitecoinDefaultElectrumServer({required Box<Node> nodes}) {
|
Node? getLitecoinDefaultElectrumServer({required Box<Node> nodes}) {
|
||||||
return nodes.values.firstWhereOrNull(
|
return nodes.values
|
||||||
(Node node) => node.uriRaw == cakeWalletLitecoinElectrumUri)
|
.firstWhereOrNull((Node node) => node.uriRaw == cakeWalletLitecoinElectrumUri) ??
|
||||||
?? nodes.values.firstWhereOrNull((node) => node.type == WalletType.litecoin);
|
nodes.values.firstWhereOrNull((node) => node.type == WalletType.litecoin);
|
||||||
}
|
}
|
||||||
|
|
||||||
Node? getHavenDefaultNode({required Box<Node> nodes}) {
|
Node? getHavenDefaultNode({required Box<Node> nodes}) {
|
||||||
return nodes.values.firstWhereOrNull(
|
return nodes.values.firstWhereOrNull((Node node) => node.uriRaw == havenDefaultNodeUri) ??
|
||||||
(Node node) => node.uriRaw == havenDefaultNodeUri)
|
nodes.values.firstWhereOrNull((node) => node.type == WalletType.haven);
|
||||||
?? nodes.values.firstWhereOrNull((node) => node.type == WalletType.haven);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Node? getEthereumDefaultNode({required Box<Node> nodes}) {
|
Node? getEthereumDefaultNode({required Box<Node> nodes}) {
|
||||||
return nodes.values.firstWhereOrNull(
|
return nodes.values.firstWhereOrNull((Node node) => node.uriRaw == ethereumDefaultNodeUri) ??
|
||||||
(Node node) => node.uriRaw == ethereumDefaultNodeUri)
|
nodes.values.firstWhereOrNull((node) => node.type == WalletType.ethereum);
|
||||||
?? nodes.values.firstWhereOrNull((node) => node.type == WalletType.ethereum);
|
}
|
||||||
|
|
||||||
|
Node? getNanoDefaultNode({required Box<Node> nodes}) {
|
||||||
|
return nodes.values.firstWhereOrNull((Node node) => node.uriRaw == nanoDefaultNodeUri) ??
|
||||||
|
nodes.values.firstWhereOrNull((node) => node.type == WalletType.nano);
|
||||||
|
}
|
||||||
|
|
||||||
|
Node? getNanoDefaultPowNode({required Box<Node> nodes}) {
|
||||||
|
return nodes.values.firstWhereOrNull((Node node) => node.uriRaw == nanoDefaultPowNodeUri) ??
|
||||||
|
nodes.values.firstWhereOrNull((node) => (node.type == WalletType.nano));
|
||||||
}
|
}
|
||||||
|
|
||||||
Node getMoneroDefaultNode({required Box<Node> nodes}) {
|
Node getMoneroDefaultNode({required Box<Node> nodes}) {
|
||||||
|
@ -329,16 +336,14 @@ Node getMoneroDefaultNode({required Box<Node> nodes}) {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return nodes.values
|
return nodes.values.firstWhere((Node node) => node.uriRaw == nodeUri);
|
||||||
.firstWhere((Node node) => node.uriRaw == nodeUri);
|
} catch (_) {
|
||||||
} catch(_) {
|
|
||||||
return nodes.values.first;
|
return nodes.values.first;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> changeBitcoinCurrentElectrumServerToDefault(
|
Future<void> changeBitcoinCurrentElectrumServerToDefault(
|
||||||
{required SharedPreferences sharedPreferences,
|
{required SharedPreferences sharedPreferences, required Box<Node> nodes}) async {
|
||||||
required Box<Node> nodes}) async {
|
|
||||||
final server = getBitcoinDefaultElectrumServer(nodes: nodes);
|
final server = getBitcoinDefaultElectrumServer(nodes: nodes);
|
||||||
final serverId = server?.key as int? ?? 0;
|
final serverId = server?.key as int? ?? 0;
|
||||||
|
|
||||||
|
@ -346,8 +351,7 @@ Future<void> changeBitcoinCurrentElectrumServerToDefault(
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> changeLitecoinCurrentElectrumServerToDefault(
|
Future<void> changeLitecoinCurrentElectrumServerToDefault(
|
||||||
{required SharedPreferences sharedPreferences,
|
{required SharedPreferences sharedPreferences, required Box<Node> nodes}) async {
|
||||||
required Box<Node> nodes}) async {
|
|
||||||
final server = getLitecoinDefaultElectrumServer(nodes: nodes);
|
final server = getLitecoinDefaultElectrumServer(nodes: nodes);
|
||||||
final serverId = server?.key as int? ?? 0;
|
final serverId = server?.key as int? ?? 0;
|
||||||
|
|
||||||
|
@ -355,8 +359,7 @@ Future<void> changeLitecoinCurrentElectrumServerToDefault(
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> changeHavenCurrentNodeToDefault(
|
Future<void> changeHavenCurrentNodeToDefault(
|
||||||
{required SharedPreferences sharedPreferences,
|
{required SharedPreferences sharedPreferences, required Box<Node> nodes}) async {
|
||||||
required Box<Node> nodes}) async {
|
|
||||||
final node = getHavenDefaultNode(nodes: nodes);
|
final node = getHavenDefaultNode(nodes: nodes);
|
||||||
final nodeId = node?.key as int? ?? 0;
|
final nodeId = node?.key as int? ?? 0;
|
||||||
|
|
||||||
|
@ -364,25 +367,21 @@ Future<void> changeHavenCurrentNodeToDefault(
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> replaceDefaultNode(
|
Future<void> replaceDefaultNode(
|
||||||
{required SharedPreferences sharedPreferences,
|
{required SharedPreferences sharedPreferences, required Box<Node> nodes}) async {
|
||||||
required Box<Node> nodes}) async {
|
|
||||||
const nodesForReplace = <String>[
|
const nodesForReplace = <String>[
|
||||||
'xmr-node-uk.cakewallet.com:18081',
|
'xmr-node-uk.cakewallet.com:18081',
|
||||||
'eu-node.cakewallet.io:18081',
|
'eu-node.cakewallet.io:18081',
|
||||||
'node.cakewallet.io:18081'
|
'node.cakewallet.io:18081'
|
||||||
];
|
];
|
||||||
final currentNodeId = sharedPreferences.getInt(PreferencesKey.currentNodeIdKey);
|
final currentNodeId = sharedPreferences.getInt(PreferencesKey.currentNodeIdKey);
|
||||||
final currentNode =
|
final currentNode = nodes.values.firstWhereOrNull((Node node) => node.key == currentNodeId);
|
||||||
nodes.values.firstWhereOrNull((Node node) => node.key == currentNodeId);
|
final needToReplace = currentNode == null ? true : nodesForReplace.contains(currentNode.uriRaw);
|
||||||
final needToReplace =
|
|
||||||
currentNode == null ? true : nodesForReplace.contains(currentNode.uriRaw);
|
|
||||||
|
|
||||||
if (!needToReplace) {
|
if (!needToReplace) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await changeMoneroCurrentNodeToDefault(
|
await changeMoneroCurrentNodeToDefault(sharedPreferences: sharedPreferences, nodes: nodes);
|
||||||
sharedPreferences: sharedPreferences, nodes: nodes);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> updateNodeTypes({required Box<Node> nodes}) async {
|
Future<void> updateNodeTypes({required Box<Node> nodes}) async {
|
||||||
|
@ -421,14 +420,11 @@ Future<void> addHavenNodeList({required Box<Node> nodes}) async {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> addAddressesForMoneroWallets(
|
Future<void> addAddressesForMoneroWallets(Box<WalletInfo> walletInfoSource) async {
|
||||||
Box<WalletInfo> walletInfoSource) async {
|
final moneroWalletsInfo = walletInfoSource.values.where((info) => info.type == WalletType.monero);
|
||||||
final moneroWalletsInfo =
|
|
||||||
walletInfoSource.values.where((info) => info.type == WalletType.monero);
|
|
||||||
moneroWalletsInfo.forEach((info) async {
|
moneroWalletsInfo.forEach((info) async {
|
||||||
try {
|
try {
|
||||||
final walletPath =
|
final walletPath = await pathForWallet(name: info.name, type: WalletType.monero);
|
||||||
await pathForWallet(name: info.name, type: WalletType.monero);
|
|
||||||
final addressFilePath = '$walletPath.address.txt';
|
final addressFilePath = '$walletPath.address.txt';
|
||||||
final addressFile = File(addressFilePath);
|
final addressFile = File(addressFilePath);
|
||||||
|
|
||||||
|
@ -449,8 +445,7 @@ Future<void> updateDisplayModes(SharedPreferences sharedPreferences) async {
|
||||||
final currentBalanceDisplayMode =
|
final currentBalanceDisplayMode =
|
||||||
sharedPreferences.getInt(PreferencesKey.currentBalanceDisplayModeKey) ?? -1;
|
sharedPreferences.getInt(PreferencesKey.currentBalanceDisplayModeKey) ?? -1;
|
||||||
final balanceDisplayMode = currentBalanceDisplayMode < 2 ? 3 : 2;
|
final balanceDisplayMode = currentBalanceDisplayMode < 2 ? 3 : 2;
|
||||||
await sharedPreferences.setInt(
|
await sharedPreferences.setInt(PreferencesKey.currentBalanceDisplayModeKey, balanceDisplayMode);
|
||||||
PreferencesKey.currentBalanceDisplayModeKey, balanceDisplayMode);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> generateBackupPassword(FlutterSecureStorage secureStorage) async {
|
Future<void> generateBackupPassword(FlutterSecureStorage secureStorage) async {
|
||||||
|
@ -464,10 +459,9 @@ Future<void> generateBackupPassword(FlutterSecureStorage secureStorage) async {
|
||||||
await secureStorage.write(key: key, value: password);
|
await secureStorage.write(key: key, value: password);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> changeTransactionPriorityAndFeeRateKeys(
|
Future<void> changeTransactionPriorityAndFeeRateKeys(SharedPreferences sharedPreferences) async {
|
||||||
SharedPreferences sharedPreferences) async {
|
final legacyTransactionPriority =
|
||||||
final legacyTransactionPriority = sharedPreferences
|
sharedPreferences.getInt(PreferencesKey.currentTransactionPriorityKeyLegacy)!;
|
||||||
.getInt(PreferencesKey.currentTransactionPriorityKeyLegacy)!;
|
|
||||||
await sharedPreferences.setInt(
|
await sharedPreferences.setInt(
|
||||||
PreferencesKey.moneroTransactionPriority, legacyTransactionPriority);
|
PreferencesKey.moneroTransactionPriority, legacyTransactionPriority);
|
||||||
await sharedPreferences.setInt(PreferencesKey.bitcoinTransactionPriority,
|
await sharedPreferences.setInt(PreferencesKey.bitcoinTransactionPriority,
|
||||||
|
@ -477,10 +471,8 @@ Future<void> changeTransactionPriorityAndFeeRateKeys(
|
||||||
Future<void> changeDefaultMoneroNode(
|
Future<void> changeDefaultMoneroNode(
|
||||||
Box<Node> nodeSource, SharedPreferences sharedPreferences) async {
|
Box<Node> nodeSource, SharedPreferences sharedPreferences) async {
|
||||||
const cakeWalletMoneroNodeUriPattern = '.cakewallet.com';
|
const cakeWalletMoneroNodeUriPattern = '.cakewallet.com';
|
||||||
final currentMoneroNodeId =
|
final currentMoneroNodeId = sharedPreferences.getInt(PreferencesKey.currentNodeIdKey);
|
||||||
sharedPreferences.getInt(PreferencesKey.currentNodeIdKey);
|
final currentMoneroNode = nodeSource.values.firstWhere((node) => node.key == currentMoneroNodeId);
|
||||||
final currentMoneroNode =
|
|
||||||
nodeSource.values.firstWhere((node) => node.key == currentMoneroNodeId);
|
|
||||||
final needToReplaceCurrentMoneroNode =
|
final needToReplaceCurrentMoneroNode =
|
||||||
currentMoneroNode.uri.toString().contains(cakeWalletMoneroNodeUriPattern);
|
currentMoneroNode.uri.toString().contains(cakeWalletMoneroNodeUriPattern);
|
||||||
|
|
||||||
|
@ -491,78 +483,87 @@ Future<void> changeDefaultMoneroNode(
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
final newCakeWalletNode =
|
final newCakeWalletNode = Node(uri: newCakeWalletMoneroUri, type: WalletType.monero);
|
||||||
Node(uri: newCakeWalletMoneroUri, type: WalletType.monero);
|
|
||||||
|
|
||||||
await nodeSource.add(newCakeWalletNode);
|
await nodeSource.add(newCakeWalletNode);
|
||||||
|
|
||||||
if (needToReplaceCurrentMoneroNode) {
|
if (needToReplaceCurrentMoneroNode) {
|
||||||
await sharedPreferences.setInt(
|
await sharedPreferences.setInt(PreferencesKey.currentNodeIdKey, newCakeWalletNode.key as int);
|
||||||
PreferencesKey.currentNodeIdKey, newCakeWalletNode.key as int);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> checkCurrentNodes(
|
Future<void> checkCurrentNodes(
|
||||||
Box<Node> nodeSource, SharedPreferences sharedPreferences) async {
|
Box<Node> nodeSource, Box<Node> powNodeSource, SharedPreferences sharedPreferences) async {
|
||||||
final currentMoneroNodeId =
|
final currentMoneroNodeId = sharedPreferences.getInt(PreferencesKey.currentNodeIdKey);
|
||||||
sharedPreferences.getInt(PreferencesKey.currentNodeIdKey);
|
|
||||||
final currentBitcoinElectrumSeverId =
|
final currentBitcoinElectrumSeverId =
|
||||||
sharedPreferences.getInt(PreferencesKey.currentBitcoinElectrumSererIdKey);
|
sharedPreferences.getInt(PreferencesKey.currentBitcoinElectrumSererIdKey);
|
||||||
final currentLitecoinElectrumSeverId = sharedPreferences
|
final currentLitecoinElectrumSeverId =
|
||||||
.getInt(PreferencesKey.currentLitecoinElectrumSererIdKey);
|
sharedPreferences.getInt(PreferencesKey.currentLitecoinElectrumSererIdKey);
|
||||||
final currentHavenNodeId = sharedPreferences
|
final currentHavenNodeId = sharedPreferences.getInt(PreferencesKey.currentHavenNodeIdKey);
|
||||||
.getInt(PreferencesKey.currentHavenNodeIdKey);
|
final currentEthereumNodeId = sharedPreferences.getInt(PreferencesKey.currentEthereumNodeIdKey);
|
||||||
final currentEthereumNodeId = sharedPreferences
|
final currentNanoNodeId = sharedPreferences.getInt(PreferencesKey.currentNanoNodeIdKey);
|
||||||
.getInt(PreferencesKey.currentEthereumNodeIdKey);
|
final currentNanoPowNodeId = sharedPreferences.getInt(PreferencesKey.currentNanoPowNodeIdKey);
|
||||||
final currentMoneroNode = nodeSource.values.firstWhereOrNull(
|
final currentMoneroNode =
|
||||||
(node) => node.key == currentMoneroNodeId);
|
nodeSource.values.firstWhereOrNull((node) => node.key == currentMoneroNodeId);
|
||||||
final currentBitcoinElectrumServer = nodeSource.values.firstWhereOrNull(
|
final currentBitcoinElectrumServer =
|
||||||
(node) => node.key == currentBitcoinElectrumSeverId);
|
nodeSource.values.firstWhereOrNull((node) => node.key == currentBitcoinElectrumSeverId);
|
||||||
final currentLitecoinElectrumServer = nodeSource.values.firstWhereOrNull(
|
final currentLitecoinElectrumServer =
|
||||||
(node) => node.key == currentLitecoinElectrumSeverId);
|
nodeSource.values.firstWhereOrNull((node) => node.key == currentLitecoinElectrumSeverId);
|
||||||
final currentHavenNodeServer = nodeSource.values.firstWhereOrNull(
|
final currentHavenNodeServer =
|
||||||
(node) => node.key == currentHavenNodeId);
|
nodeSource.values.firstWhereOrNull((node) => node.key == currentHavenNodeId);
|
||||||
final currentEthereumNodeServer = nodeSource.values.firstWhereOrNull(
|
final currentEthereumNodeServer =
|
||||||
(node) => node.key == currentEthereumNodeId);
|
nodeSource.values.firstWhereOrNull((node) => node.key == currentEthereumNodeId);
|
||||||
|
final currentNanoNodeServer =
|
||||||
|
nodeSource.values.firstWhereOrNull((node) => node.key == currentNanoNodeId);
|
||||||
|
final currentNanoPowNodeServer =
|
||||||
|
powNodeSource.values.firstWhereOrNull((node) => node.key == currentNanoPowNodeId);
|
||||||
|
|
||||||
if (currentMoneroNode == null) {
|
if (currentMoneroNode == null) {
|
||||||
final newCakeWalletNode =
|
final newCakeWalletNode = Node(uri: newCakeWalletMoneroUri, type: WalletType.monero);
|
||||||
Node(uri: newCakeWalletMoneroUri, type: WalletType.monero);
|
|
||||||
await nodeSource.add(newCakeWalletNode);
|
await nodeSource.add(newCakeWalletNode);
|
||||||
await sharedPreferences.setInt(
|
await sharedPreferences.setInt(PreferencesKey.currentNodeIdKey, newCakeWalletNode.key as int);
|
||||||
PreferencesKey.currentNodeIdKey, newCakeWalletNode.key as int);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentBitcoinElectrumServer == null) {
|
if (currentBitcoinElectrumServer == null) {
|
||||||
final cakeWalletElectrum =
|
final cakeWalletElectrum = Node(uri: cakeWalletBitcoinElectrumUri, type: WalletType.bitcoin);
|
||||||
Node(uri: cakeWalletBitcoinElectrumUri, type: WalletType.bitcoin);
|
|
||||||
await nodeSource.add(cakeWalletElectrum);
|
await nodeSource.add(cakeWalletElectrum);
|
||||||
await sharedPreferences.setInt(
|
await sharedPreferences.setInt(
|
||||||
PreferencesKey.currentBitcoinElectrumSererIdKey,
|
PreferencesKey.currentBitcoinElectrumSererIdKey, cakeWalletElectrum.key as int);
|
||||||
cakeWalletElectrum.key as int);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentLitecoinElectrumServer == null) {
|
if (currentLitecoinElectrumServer == null) {
|
||||||
final cakeWalletElectrum =
|
final cakeWalletElectrum = Node(uri: cakeWalletLitecoinElectrumUri, type: WalletType.litecoin);
|
||||||
Node(uri: cakeWalletLitecoinElectrumUri, type: WalletType.litecoin);
|
|
||||||
await nodeSource.add(cakeWalletElectrum);
|
await nodeSource.add(cakeWalletElectrum);
|
||||||
await sharedPreferences.setInt(
|
await sharedPreferences.setInt(
|
||||||
PreferencesKey.currentLitecoinElectrumSererIdKey,
|
PreferencesKey.currentLitecoinElectrumSererIdKey, cakeWalletElectrum.key as int);
|
||||||
cakeWalletElectrum.key as int);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentHavenNodeServer == null) {
|
if (currentHavenNodeServer == null) {
|
||||||
final node = Node(uri: havenDefaultNodeUri, type: WalletType.haven);
|
final node = Node(uri: havenDefaultNodeUri, type: WalletType.haven);
|
||||||
await nodeSource.add(node);
|
await nodeSource.add(node);
|
||||||
await sharedPreferences.setInt(
|
await sharedPreferences.setInt(PreferencesKey.currentHavenNodeIdKey, node.key as int);
|
||||||
PreferencesKey.currentHavenNodeIdKey, node.key as int);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentEthereumNodeServer == null) {
|
if (currentEthereumNodeServer == null) {
|
||||||
final node = Node(uri: ethereumDefaultNodeUri, type: WalletType.ethereum);
|
final node = Node(uri: ethereumDefaultNodeUri, type: WalletType.ethereum);
|
||||||
await nodeSource.add(node);
|
await nodeSource.add(node);
|
||||||
await sharedPreferences.setInt(
|
await sharedPreferences.setInt(PreferencesKey.currentEthereumNodeIdKey, node.key as int);
|
||||||
PreferencesKey.currentEthereumNodeIdKey, node.key as int);
|
}
|
||||||
|
|
||||||
|
if (currentNanoNodeServer == null) {
|
||||||
|
final node = Node(uri: nanoDefaultNodeUri, useSSL: true, type: WalletType.nano);
|
||||||
|
await nodeSource.add(node);
|
||||||
|
await sharedPreferences.setInt(PreferencesKey.currentNanoNodeIdKey, node.key as int);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentNanoPowNodeServer == null) {
|
||||||
|
Node? node = powNodeSource.values
|
||||||
|
.firstWhereOrNull((node) => node.uri.toString() == nanoDefaultPowNodeUri);
|
||||||
|
if (node == null) {
|
||||||
|
node = Node(uri: nanoDefaultPowNodeUri, useSSL: true, type: WalletType.nano);
|
||||||
|
await powNodeSource.add(node);
|
||||||
|
}
|
||||||
|
await sharedPreferences.setInt(PreferencesKey.currentNanoPowNodeIdKey, node.key as int);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -570,31 +571,27 @@ Future<void> resetBitcoinElectrumServer(
|
||||||
Box<Node> nodeSource, SharedPreferences sharedPreferences) async {
|
Box<Node> nodeSource, SharedPreferences sharedPreferences) async {
|
||||||
final currentElectrumSeverId =
|
final currentElectrumSeverId =
|
||||||
sharedPreferences.getInt(PreferencesKey.currentBitcoinElectrumSererIdKey);
|
sharedPreferences.getInt(PreferencesKey.currentBitcoinElectrumSererIdKey);
|
||||||
final oldElectrumServer = nodeSource.values.firstWhereOrNull(
|
final oldElectrumServer = nodeSource.values
|
||||||
(node) => node.uri.toString().contains('electrumx.cakewallet.com'));
|
.firstWhereOrNull((node) => node.uri.toString().contains('electrumx.cakewallet.com'));
|
||||||
var cakeWalletNode = nodeSource.values.firstWhereOrNull(
|
var cakeWalletNode = nodeSource.values
|
||||||
(node) => node.uriRaw.toString() == cakeWalletBitcoinElectrumUri);
|
.firstWhereOrNull((node) => node.uriRaw.toString() == cakeWalletBitcoinElectrumUri);
|
||||||
|
|
||||||
if (cakeWalletNode == null) {
|
if (cakeWalletNode == null) {
|
||||||
cakeWalletNode =
|
cakeWalletNode = Node(uri: cakeWalletBitcoinElectrumUri, type: WalletType.bitcoin);
|
||||||
Node(uri: cakeWalletBitcoinElectrumUri, type: WalletType.bitcoin);
|
|
||||||
await nodeSource.add(cakeWalletNode);
|
await nodeSource.add(cakeWalletNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentElectrumSeverId == oldElectrumServer?.key) {
|
if (currentElectrumSeverId == oldElectrumServer?.key) {
|
||||||
await sharedPreferences.setInt(
|
await sharedPreferences.setInt(
|
||||||
PreferencesKey.currentBitcoinElectrumSererIdKey,
|
PreferencesKey.currentBitcoinElectrumSererIdKey, cakeWalletNode.key as int);
|
||||||
cakeWalletNode.key as int);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await oldElectrumServer?.delete();
|
await oldElectrumServer?.delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> changeDefaultHavenNode(
|
Future<void> changeDefaultHavenNode(Box<Node> nodeSource) async {
|
||||||
Box<Node> nodeSource) async {
|
|
||||||
const previousHavenDefaultNodeUri = 'vault.havenprotocol.org:443';
|
const previousHavenDefaultNodeUri = 'vault.havenprotocol.org:443';
|
||||||
final havenNodes = nodeSource.values.where(
|
final havenNodes = nodeSource.values.where((node) => node.uriRaw == previousHavenDefaultNodeUri);
|
||||||
(node) => node.uriRaw == previousHavenDefaultNodeUri);
|
|
||||||
havenNodes.forEach((node) async {
|
havenNodes.forEach((node) async {
|
||||||
node.uriRaw = havenDefaultNodeUri;
|
node.uriRaw = havenDefaultNodeUri;
|
||||||
await node.save();
|
await node.save();
|
||||||
|
@ -607,8 +604,8 @@ Future<void> migrateExchangeStatus(SharedPreferences sharedPreferences) async {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await sharedPreferences.setInt(PreferencesKey.exchangeStatusKey, isExchangeDisabled
|
await sharedPreferences.setInt(PreferencesKey.exchangeStatusKey,
|
||||||
? ExchangeApiMode.disabled.raw : ExchangeApiMode.enabled.raw);
|
isExchangeDisabled ? ExchangeApiMode.disabled.raw : ExchangeApiMode.enabled.raw);
|
||||||
|
|
||||||
await sharedPreferences.remove(PreferencesKey.disableExchangeKey);
|
await sharedPreferences.remove(PreferencesKey.disableExchangeKey);
|
||||||
}
|
}
|
||||||
|
@ -623,10 +620,42 @@ Future<void> addEthereumNodeList({required Box<Node> nodes}) async {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> changeEthereumCurrentNodeToDefault(
|
Future<void> changeEthereumCurrentNodeToDefault(
|
||||||
{required SharedPreferences sharedPreferences,
|
{required SharedPreferences sharedPreferences, required Box<Node> nodes}) async {
|
||||||
required Box<Node> nodes}) async {
|
|
||||||
final node = getEthereumDefaultNode(nodes: nodes);
|
final node = getEthereumDefaultNode(nodes: nodes);
|
||||||
final nodeId = node?.key as int? ?? 0;
|
final nodeId = node?.key as int? ?? 0;
|
||||||
|
|
||||||
await sharedPreferences.setInt(PreferencesKey.currentEthereumNodeIdKey, nodeId);
|
await sharedPreferences.setInt(PreferencesKey.currentEthereumNodeIdKey, nodeId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> addNanoNodeList({required Box<Node> nodes}) async {
|
||||||
|
final nodeList = await loadDefaultNanoNodes();
|
||||||
|
for (var node in nodeList) {
|
||||||
|
if (nodes.values.firstWhereOrNull((element) => element.uriRaw == node.uriRaw) == null) {
|
||||||
|
await nodes.add(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> addNanoPowNodeList({required Box<Node> nodes}) async {
|
||||||
|
final nodeList = await loadDefaultNanoPowNodes();
|
||||||
|
for (var node in nodeList) {
|
||||||
|
if (nodes.values.firstWhereOrNull((element) => element.uriRaw == node.uriRaw) == null) {
|
||||||
|
await nodes.add(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> changeNanoCurrentNodeToDefault(
|
||||||
|
{required SharedPreferences sharedPreferences, required Box<Node> nodes}) async {
|
||||||
|
final node = getNanoDefaultNode(nodes: nodes);
|
||||||
|
final nodeId = node?.key as int? ?? 0;
|
||||||
|
|
||||||
|
await sharedPreferences.setInt(PreferencesKey.currentNanoNodeIdKey, nodeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> changeNanoCurrentPowNodeToDefault(
|
||||||
|
{required SharedPreferences sharedPreferences, required Box<Node> nodes}) async {
|
||||||
|
final node = getNanoDefaultPowNode(nodes: nodes);
|
||||||
|
final nodeId = node?.key as int? ?? 0;
|
||||||
|
await sharedPreferences.setInt(PreferencesKey.currentNanoPowNodeIdKey, nodeId);
|
||||||
|
}
|
||||||
|
|
|
@ -52,6 +52,8 @@ class MainActions {
|
||||||
case WalletType.bitcoin:
|
case WalletType.bitcoin:
|
||||||
case WalletType.litecoin:
|
case WalletType.litecoin:
|
||||||
case WalletType.ethereum:
|
case WalletType.ethereum:
|
||||||
|
case WalletType.nano:
|
||||||
|
case WalletType.banano:
|
||||||
switch (defaultBuyProvider) {
|
switch (defaultBuyProvider) {
|
||||||
case BuyProviderType.AskEachTime:
|
case BuyProviderType.AskEachTime:
|
||||||
Navigator.pushNamed(context, Routes.buy);
|
Navigator.pushNamed(context, Routes.buy);
|
||||||
|
@ -73,7 +75,7 @@ class MainActions {
|
||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
return AlertWithOneAction(
|
return AlertWithOneAction(
|
||||||
alertTitle: S.of(context).buy,
|
alertTitle: S.of(context).buy,
|
||||||
alertContent: S.of(context).buy_alert_content,
|
alertContent: S.of(context).unsupported_asset,
|
||||||
buttonText: S.of(context).ok,
|
buttonText: S.of(context).ok,
|
||||||
buttonAction: () => Navigator.of(context).pop());
|
buttonAction: () => Navigator.of(context).pop());
|
||||||
});
|
});
|
||||||
|
@ -143,7 +145,7 @@ class MainActions {
|
||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
return AlertWithOneAction(
|
return AlertWithOneAction(
|
||||||
alertTitle: S.of(context).sell,
|
alertTitle: S.of(context).sell,
|
||||||
alertContent: S.of(context).sell_alert_content,
|
alertContent: S.of(context).unsupported_asset,
|
||||||
buttonText: S.of(context).ok,
|
buttonText: S.of(context).ok,
|
||||||
buttonAction: () => Navigator.of(context).pop());
|
buttonAction: () => Navigator.of(context).pop());
|
||||||
},
|
},
|
||||||
|
|
|
@ -21,13 +21,12 @@ Future<List<Node>> loadDefaultNodes() async {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<Node>> loadBitcoinElectrumServerList() async {
|
Future<List<Node>> loadBitcoinElectrumServerList() async {
|
||||||
final serverListRaw =
|
final serverListRaw = await rootBundle.loadString('assets/bitcoin_electrum_server_list.yml');
|
||||||
await rootBundle.loadString('assets/bitcoin_electrum_server_list.yml');
|
|
||||||
final loadedServerList = loadYaml(serverListRaw) as YamlList;
|
final loadedServerList = loadYaml(serverListRaw) as YamlList;
|
||||||
final serverList = <Node>[];
|
final serverList = <Node>[];
|
||||||
|
|
||||||
for (final raw in loadedServerList) {
|
for (final raw in loadedServerList) {
|
||||||
if (raw is Map) {
|
if (raw is Map) {
|
||||||
final node = Node.fromMap(Map<String, Object>.from(raw));
|
final node = Node.fromMap(Map<String, Object>.from(raw));
|
||||||
node.type = WalletType.bitcoin;
|
node.type = WalletType.bitcoin;
|
||||||
serverList.add(node);
|
serverList.add(node);
|
||||||
|
@ -38,8 +37,7 @@ Future<List<Node>> loadBitcoinElectrumServerList() async {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<Node>> loadLitecoinElectrumServerList() async {
|
Future<List<Node>> loadLitecoinElectrumServerList() async {
|
||||||
final serverListRaw =
|
final serverListRaw = await rootBundle.loadString('assets/litecoin_electrum_server_list.yml');
|
||||||
await rootBundle.loadString('assets/litecoin_electrum_server_list.yml');
|
|
||||||
final loadedServerList = loadYaml(serverListRaw) as YamlList;
|
final loadedServerList = loadYaml(serverListRaw) as YamlList;
|
||||||
final serverList = <Node>[];
|
final serverList = <Node>[];
|
||||||
|
|
||||||
|
@ -86,17 +84,60 @@ Future<List<Node>> loadDefaultEthereumNodes() async {
|
||||||
return nodes;
|
return nodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<List<Node>> loadDefaultNanoNodes() async {
|
||||||
|
final nodesRaw = await rootBundle.loadString('assets/nano_node_list.yml');
|
||||||
|
final loadedNodes = loadYaml(nodesRaw) as YamlList;
|
||||||
|
final nodes = <Node>[];
|
||||||
|
|
||||||
|
for (final raw in loadedNodes) {
|
||||||
|
if (raw is Map) {
|
||||||
|
final node = Node.fromMap(Map<String, Object>.from(raw));
|
||||||
|
node.type = WalletType.nano;
|
||||||
|
nodes.add(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<List<Node>> loadDefaultNanoPowNodes() async {
|
||||||
|
final powNodesRaw = await rootBundle.loadString('assets/nano_pow_node_list.yml');
|
||||||
|
final loadedPowNodes = loadYaml(powNodesRaw) as YamlList;
|
||||||
|
final nodes = <Node>[];
|
||||||
|
|
||||||
|
for (final raw in loadedPowNodes) {
|
||||||
|
if (raw is Map) {
|
||||||
|
final node = Node.fromMap(Map<String, Object>.from(raw));
|
||||||
|
node.type = WalletType.nano;
|
||||||
|
nodes.add(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nodes;
|
||||||
|
}
|
||||||
|
|
||||||
Future resetToDefault(Box<Node> nodeSource) async {
|
Future resetToDefault(Box<Node> nodeSource) async {
|
||||||
final moneroNodes = await loadDefaultNodes();
|
final moneroNodes = await loadDefaultNodes();
|
||||||
final bitcoinElectrumServerList = await loadBitcoinElectrumServerList();
|
final bitcoinElectrumServerList = await loadBitcoinElectrumServerList();
|
||||||
final litecoinElectrumServerList = await loadLitecoinElectrumServerList();
|
final litecoinElectrumServerList = await loadLitecoinElectrumServerList();
|
||||||
final havenNodes = await loadDefaultHavenNodes();
|
final havenNodes = await loadDefaultHavenNodes();
|
||||||
final nodes =
|
final ethereumNodes = await loadDefaultEthereumNodes();
|
||||||
moneroNodes +
|
final nanoNodes = await loadDefaultNanoNodes();
|
||||||
|
|
||||||
|
final nodes = moneroNodes +
|
||||||
bitcoinElectrumServerList +
|
bitcoinElectrumServerList +
|
||||||
litecoinElectrumServerList +
|
litecoinElectrumServerList +
|
||||||
havenNodes;
|
havenNodes +
|
||||||
|
ethereumNodes +
|
||||||
|
nanoNodes;
|
||||||
|
|
||||||
await nodeSource.clear();
|
await nodeSource.clear();
|
||||||
await nodeSource.addAll(nodes);
|
await nodeSource.addAll(nodes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future resetPowToDefault(Box<Node> powNodeSource) async {
|
||||||
|
final nanoPowNodes = await loadDefaultNanoPowNodes();
|
||||||
|
final nodes = nanoPowNodes;
|
||||||
|
await powNodeSource.clear();
|
||||||
|
await powNodeSource.addAll(nodes);
|
||||||
|
}
|
|
@ -6,6 +6,10 @@ class PreferencesKey {
|
||||||
static const currentLitecoinElectrumSererIdKey = 'current_node_id_ltc';
|
static const currentLitecoinElectrumSererIdKey = 'current_node_id_ltc';
|
||||||
static const currentHavenNodeIdKey = 'current_node_id_xhv';
|
static const currentHavenNodeIdKey = 'current_node_id_xhv';
|
||||||
static const currentEthereumNodeIdKey = 'current_node_id_eth';
|
static const currentEthereumNodeIdKey = 'current_node_id_eth';
|
||||||
|
static const currentNanoNodeIdKey = 'current_node_id_nano';
|
||||||
|
static const currentNanoPowNodeIdKey = 'current_node_id_nano_pow';
|
||||||
|
static const currentBananoNodeIdKey = 'current_node_id_banano';
|
||||||
|
static const currentBananoPowNodeIdKey = 'current_node_id_banano_pow';
|
||||||
static const currentFiatCurrencyKey = 'current_fiat_currency';
|
static const currentFiatCurrencyKey = 'current_fiat_currency';
|
||||||
static const currentTransactionPriorityKeyLegacy = 'current_fee_priority';
|
static const currentTransactionPriorityKeyLegacy = 'current_fee_priority';
|
||||||
static const currentBalanceDisplayModeKey = 'current_balance_display_mode';
|
static const currentBalanceDisplayModeKey = 'current_balance_display_mode';
|
||||||
|
|
|
@ -17,8 +17,11 @@ List<TransactionPriority> priorityForWalletType(WalletType type) {
|
||||||
return haven!.getTransactionPriorities();
|
return haven!.getTransactionPriorities();
|
||||||
case WalletType.ethereum:
|
case WalletType.ethereum:
|
||||||
return ethereum!.getTransactionPriorities();
|
return ethereum!.getTransactionPriorities();
|
||||||
|
// no such thing for nano/banano:
|
||||||
|
case WalletType.nano:
|
||||||
|
case WalletType.banano:
|
||||||
|
return [];
|
||||||
default:
|
default:
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -97,6 +97,10 @@ Future<void> initializeAppConfigs() async {
|
||||||
CakeHive.registerAdapter(WalletInfoAdapter());
|
CakeHive.registerAdapter(WalletInfoAdapter());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!Hive.isAdapterRegistered(DERIVATION_TYPE_TYPE_ID)) {
|
||||||
|
CakeHive.registerAdapter(DerivationTypeAdapter());
|
||||||
|
}
|
||||||
|
|
||||||
if (!CakeHive.isAdapterRegistered(WALLET_TYPE_TYPE_ID)) {
|
if (!CakeHive.isAdapterRegistered(WALLET_TYPE_TYPE_ID)) {
|
||||||
CakeHive.registerAdapter(WalletTypeAdapter());
|
CakeHive.registerAdapter(WalletTypeAdapter());
|
||||||
}
|
}
|
||||||
|
@ -128,6 +132,7 @@ Future<void> initializeAppConfigs() async {
|
||||||
final ordersBoxKey = await getEncryptionKey(secureStorage: secureStorage, forKey: Order.boxKey);
|
final ordersBoxKey = await getEncryptionKey(secureStorage: secureStorage, forKey: Order.boxKey);
|
||||||
final contacts = await CakeHive.openBox<Contact>(Contact.boxName);
|
final contacts = await CakeHive.openBox<Contact>(Contact.boxName);
|
||||||
final nodes = await CakeHive.openBox<Node>(Node.boxName);
|
final nodes = await CakeHive.openBox<Node>(Node.boxName);
|
||||||
|
final powNodes = await CakeHive.openBox<Node>(Node.boxName + "pow");// must be different from Node.boxName
|
||||||
final transactionDescriptions = await CakeHive.openBox<TransactionDescription>(
|
final transactionDescriptions = await CakeHive.openBox<TransactionDescription>(
|
||||||
TransactionDescription.boxName,
|
TransactionDescription.boxName,
|
||||||
encryptionKey: transactionDescriptionsBoxKey);
|
encryptionKey: transactionDescriptionsBoxKey);
|
||||||
|
@ -142,6 +147,7 @@ Future<void> initializeAppConfigs() async {
|
||||||
await initialSetup(
|
await initialSetup(
|
||||||
sharedPreferences: await SharedPreferences.getInstance(),
|
sharedPreferences: await SharedPreferences.getInstance(),
|
||||||
nodes: nodes,
|
nodes: nodes,
|
||||||
|
powNodes: powNodes,
|
||||||
walletInfoSource: walletInfoSource,
|
walletInfoSource: walletInfoSource,
|
||||||
contactSource: contacts,
|
contactSource: contacts,
|
||||||
tradesSource: trades,
|
tradesSource: trades,
|
||||||
|
@ -153,12 +159,13 @@ Future<void> initializeAppConfigs() async {
|
||||||
transactionDescriptions: transactionDescriptions,
|
transactionDescriptions: transactionDescriptions,
|
||||||
secureStorage: secureStorage,
|
secureStorage: secureStorage,
|
||||||
anonpayInvoiceInfo: anonpayInvoiceInfo,
|
anonpayInvoiceInfo: anonpayInvoiceInfo,
|
||||||
initialMigrationVersion: 21);
|
initialMigrationVersion: 22);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> initialSetup(
|
Future<void> initialSetup(
|
||||||
{required SharedPreferences sharedPreferences,
|
{required SharedPreferences sharedPreferences,
|
||||||
required Box<Node> nodes,
|
required Box<Node> nodes,
|
||||||
|
required Box<Node> powNodes,
|
||||||
required Box<WalletInfo> walletInfoSource,
|
required Box<WalletInfo> walletInfoSource,
|
||||||
required Box<Contact> contactSource,
|
required Box<Contact> contactSource,
|
||||||
required Box<Trade> tradesSource,
|
required Box<Trade> tradesSource,
|
||||||
|
@ -179,10 +186,12 @@ Future<void> initialSetup(
|
||||||
walletInfoSource: walletInfoSource,
|
walletInfoSource: walletInfoSource,
|
||||||
contactSource: contactSource,
|
contactSource: contactSource,
|
||||||
tradeSource: tradesSource,
|
tradeSource: tradesSource,
|
||||||
nodes: nodes);
|
nodes: nodes,
|
||||||
|
powNodes: powNodes);
|
||||||
await setup(
|
await setup(
|
||||||
walletInfoSource: walletInfoSource,
|
walletInfoSource: walletInfoSource,
|
||||||
nodeSource: nodes,
|
nodeSource: nodes,
|
||||||
|
powNodeSource: powNodes,
|
||||||
contactSource: contactSource,
|
contactSource: contactSource,
|
||||||
tradesSource: tradesSource,
|
tradesSource: tradesSource,
|
||||||
templates: templates,
|
templates: templates,
|
||||||
|
|
499
lib/nano/cw_nano.dart
Normal file
499
lib/nano/cw_nano.dart
Normal file
|
@ -0,0 +1,499 @@
|
||||||
|
part of 'nano.dart';
|
||||||
|
|
||||||
|
class CWNanoAccountList extends NanoAccountList {
|
||||||
|
CWNanoAccountList(this._wallet);
|
||||||
|
final Object _wallet;
|
||||||
|
|
||||||
|
@override
|
||||||
|
@computed
|
||||||
|
ObservableList<NanoAccount> get accounts {
|
||||||
|
final nanoWallet = _wallet as NanoWallet;
|
||||||
|
final accounts = nanoWallet.walletAddresses.accountList.accounts
|
||||||
|
.map((acc) => NanoAccount(id: acc.id, label: acc.label, balance: acc.balance))
|
||||||
|
.toList();
|
||||||
|
return ObservableList<NanoAccount>.of(accounts);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void update(Object wallet) {
|
||||||
|
final nanoWallet = wallet as NanoWallet;
|
||||||
|
nanoWallet.walletAddresses.accountList.update(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void refresh(Object wallet) {
|
||||||
|
final nanoWallet = wallet as NanoWallet;
|
||||||
|
nanoWallet.walletAddresses.accountList.refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<List<NanoAccount>> getAll(Object wallet) async {
|
||||||
|
final nanoWallet = wallet as NanoWallet;
|
||||||
|
return (await nanoWallet.walletAddresses.accountList.getAll())
|
||||||
|
.map((acc) => NanoAccount(id: acc.id, label: acc.label, balance: acc.balance))
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> addAccount(Object wallet, {required String label}) async {
|
||||||
|
final nanoWallet = wallet as NanoWallet;
|
||||||
|
await nanoWallet.walletAddresses.accountList.addAccount(label: label);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> setLabelAccount(Object wallet,
|
||||||
|
{required int accountIndex, required String label}) async {
|
||||||
|
final nanoWallet = wallet as NanoWallet;
|
||||||
|
await nanoWallet.walletAddresses.accountList
|
||||||
|
.setLabelAccount(accountIndex: accountIndex, label: label);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CWNano extends Nano {
|
||||||
|
@override
|
||||||
|
NanoAccountList getAccountList(Object wallet) {
|
||||||
|
return CWNanoAccountList(wallet);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Account getCurrentAccount(Object wallet) {
|
||||||
|
final nanoWallet = wallet as NanoWallet;
|
||||||
|
final acc = nanoWallet.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 nanoWallet = wallet as NanoWallet;
|
||||||
|
nanoWallet.walletAddresses.account = NanoAccount(id: id, label: label, balance: balance);
|
||||||
|
nanoWallet.regenerateAddress();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<String> getNanoWordList(String language) {
|
||||||
|
return NanoMnemomics.WORDLIST;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
WalletService createNanoWalletService(Box<WalletInfo> walletInfoSource) {
|
||||||
|
return NanoWalletService(walletInfoSource);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, String> getKeys(Object wallet) {
|
||||||
|
final nanoWallet = wallet as NanoWallet;
|
||||||
|
final keys = nanoWallet.keys;
|
||||||
|
return <String, String>{
|
||||||
|
"seedKey": keys.seedKey,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
WalletCredentials createNanoNewWalletCredentials({
|
||||||
|
required String name,
|
||||||
|
String? password,
|
||||||
|
}) =>
|
||||||
|
NanoNewWalletCredentials(
|
||||||
|
name: name,
|
||||||
|
password: password,
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
WalletCredentials createNanoRestoreWalletFromSeedCredentials({
|
||||||
|
required String name,
|
||||||
|
required String password,
|
||||||
|
required String mnemonic,
|
||||||
|
DerivationType? derivationType,
|
||||||
|
}) {
|
||||||
|
if (derivationType == null) {
|
||||||
|
// figure out the derivation type as best we can, otherwise set it to "unknown"
|
||||||
|
if (mnemonic.split(" ").length == 12) {
|
||||||
|
derivationType = DerivationType.bip39;
|
||||||
|
} else {
|
||||||
|
derivationType = DerivationType.unknown;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NanoRestoreWalletFromSeedCredentials(
|
||||||
|
name: name,
|
||||||
|
password: password,
|
||||||
|
mnemonic: mnemonic,
|
||||||
|
derivationType: derivationType,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
WalletCredentials createNanoRestoreWalletFromKeysCredentials({
|
||||||
|
required String name,
|
||||||
|
required String password,
|
||||||
|
required String seedKey,
|
||||||
|
DerivationType? derivationType,
|
||||||
|
}) {
|
||||||
|
if (derivationType == null) {
|
||||||
|
// figure out the derivation type as best we can, otherwise set it to "unknown"
|
||||||
|
if (seedKey.length == 64) {
|
||||||
|
derivationType = DerivationType.nano;
|
||||||
|
} else {
|
||||||
|
derivationType = DerivationType.unknown;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NanoRestoreWalletFromKeysCredentials(
|
||||||
|
name: name,
|
||||||
|
password: password,
|
||||||
|
seedKey: seedKey,
|
||||||
|
derivationType: derivationType,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Object createNanoTransactionCredentials(List<Output> outputs) {
|
||||||
|
return NanoTransactionCredentials(
|
||||||
|
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(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> changeRep(Object wallet, String address) async {
|
||||||
|
return (wallet as NanoWallet).changeRep(address);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> updateTransactions(Object wallet) async {
|
||||||
|
return (wallet as NanoWallet).updateTransactions();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
BigInt getTransactionAmountRaw(TransactionInfo transactionInfo) {
|
||||||
|
return (transactionInfo as NanoTransactionInfo).amountRaw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CWNanoUtil extends NanoUtil {
|
||||||
|
// standard:
|
||||||
|
@override
|
||||||
|
String seedToPrivate(String seed, int index) {
|
||||||
|
return ND.NanoKeys.seedToPrivate(seed, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String seedToAddress(String seed, int index) {
|
||||||
|
return ND.NanoAccounts.createAccount(
|
||||||
|
ND.NanoAccountType.NANO, privateKeyToPublic(seedToPrivate(seed, index)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String seedToMnemonic(String seed) {
|
||||||
|
return NanoMnemomics.seedToMnemonic(seed).join(" ");
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<String> mnemonicToSeed(String mnemonic) async {
|
||||||
|
return NanoMnemomics.mnemonicListToSeed(mnemonic.split(' '));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String privateKeyToPublic(String privateKey) {
|
||||||
|
// return NanoHelpers.byteToHex(Ed25519Blake2b.getPubkey(NanoHelpers.hexToBytes(privateKey))!);
|
||||||
|
return ND.NanoKeys.createPublicKey(privateKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String addressToPublicKey(String publicAddress) {
|
||||||
|
return ND.NanoAccounts.extractPublicKey(publicAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
// universal:
|
||||||
|
@override
|
||||||
|
String privateKeyToAddress(String privateKey) {
|
||||||
|
return ND.NanoAccounts.createAccount(ND.NanoAccountType.NANO, privateKeyToPublic(privateKey));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String publicKeyToAddress(String publicKey) {
|
||||||
|
return ND.NanoAccounts.createAccount(ND.NanoAccountType.NANO, publicKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
// standard + hd:
|
||||||
|
@override
|
||||||
|
bool isValidSeed(String seed) {
|
||||||
|
// Ensure seed is 64 or 128 characters long
|
||||||
|
if (seed.length != 64 && seed.length != 128) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Ensure seed only contains hex characters, 0-9;A-F
|
||||||
|
return ND.NanoHelpers.isHexString(seed);
|
||||||
|
}
|
||||||
|
|
||||||
|
// hd:
|
||||||
|
@override
|
||||||
|
Future<String> hdMnemonicListToSeed(List<String> words) async {
|
||||||
|
// if (words.length != 24) {
|
||||||
|
// throw Exception('Expected a 24-word list, got a ${words.length} list');
|
||||||
|
// }
|
||||||
|
final Uint8List salt = Uint8List.fromList(utf8.encode('mnemonic'));
|
||||||
|
final Pbkdf2 hasher = Pbkdf2(iterations: 2048);
|
||||||
|
final String seed = await hasher.sha512(words.join(' '), salt);
|
||||||
|
return seed;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<String> hdSeedToPrivate(String seed, int index) async {
|
||||||
|
List<int> seedBytes = hex.decode(seed);
|
||||||
|
KeyData data = await ED25519_HD_KEY.derivePath("m/44'/165'/$index'", seedBytes);
|
||||||
|
return hex.encode(data.key);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<String> hdSeedToAddress(String seed, int index) async {
|
||||||
|
return ND.NanoAccounts.createAccount(
|
||||||
|
ND.NanoAccountType.NANO, privateKeyToPublic(await hdSeedToPrivate(seed, index)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<String> uniSeedToAddress(String seed, int index, String type) {
|
||||||
|
if (type == "standard") {
|
||||||
|
return Future<String>.value(seedToAddress(seed, index));
|
||||||
|
} else if (type == "hd") {
|
||||||
|
return hdSeedToAddress(seed, index);
|
||||||
|
} else {
|
||||||
|
throw Exception('Unknown seed type');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<String> uniSeedToPrivate(String seed, int index, String type) {
|
||||||
|
if (type == "standard") {
|
||||||
|
return Future<String>.value(seedToPrivate(seed, index));
|
||||||
|
} else if (type == "hd") {
|
||||||
|
return hdSeedToPrivate(seed, index);
|
||||||
|
} else {
|
||||||
|
throw Exception('Unknown seed type');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool isValidBip39Seed(String seed) {
|
||||||
|
// Ensure seed is 128 characters long
|
||||||
|
if (seed.length != 128) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Ensure seed only contains hex characters, 0-9;A-F
|
||||||
|
return ND.NanoHelpers.isHexString(seed);
|
||||||
|
}
|
||||||
|
|
||||||
|
// number util:
|
||||||
|
|
||||||
|
static const int maxDecimalDigits = 6; // Max digits after decimal
|
||||||
|
BigInt rawPerNano = BigInt.parse("1000000000000000000000000000000");
|
||||||
|
BigInt rawPerNyano = BigInt.parse("1000000000000000000000000");
|
||||||
|
BigInt rawPerBanano = BigInt.parse("100000000000000000000000000000");
|
||||||
|
BigInt rawPerXMR = BigInt.parse("1000000000000");
|
||||||
|
BigInt convertXMRtoNano = BigInt.parse("1000000000000000000");
|
||||||
|
// static BigInt convertXMRtoNano = BigInt.parse("1000000000000000000000000000");
|
||||||
|
|
||||||
|
/// Convert raw to ban and return as BigDecimal
|
||||||
|
///
|
||||||
|
/// @param raw 100000000000000000000000000000
|
||||||
|
/// @return Decimal value 1.000000000000000000000000000000
|
||||||
|
///
|
||||||
|
@override
|
||||||
|
Decimal getRawAsDecimal(String? raw, BigInt? rawPerCur) {
|
||||||
|
rawPerCur ??= rawPerNano;
|
||||||
|
final Decimal amount = Decimal.parse(raw.toString());
|
||||||
|
final Decimal result = (amount / Decimal.parse(rawPerCur.toString())).toDecimal();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String truncateDecimal(Decimal input, {int digits = maxDecimalDigits}) {
|
||||||
|
Decimal bigger = input.shift(digits);
|
||||||
|
bigger = bigger.floor(); // chop off the decimal: 1.059 -> 1.05
|
||||||
|
bigger = bigger.shift(-digits);
|
||||||
|
return bigger.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return raw as a NANO amount.
|
||||||
|
///
|
||||||
|
/// @param raw 100000000000000000000000000000
|
||||||
|
/// @returns 1
|
||||||
|
///
|
||||||
|
@override
|
||||||
|
String getRawAsUsableString(String? raw, BigInt rawPerCur) {
|
||||||
|
final String res =
|
||||||
|
truncateDecimal(getRawAsDecimal(raw, rawPerCur), digits: maxDecimalDigits + 9);
|
||||||
|
|
||||||
|
if (raw == null || raw == "0" || raw == "00000000000000000000000000000000") {
|
||||||
|
return "0";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!res.contains(".")) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
final String numAmount = res.split(".")[0];
|
||||||
|
String decAmount = res.split(".")[1];
|
||||||
|
|
||||||
|
// truncate:
|
||||||
|
if (decAmount.length > maxDecimalDigits) {
|
||||||
|
decAmount = decAmount.substring(0, maxDecimalDigits);
|
||||||
|
// remove trailing zeros:
|
||||||
|
decAmount = decAmount.replaceAllMapped(RegExp(r'0+$'), (Match match) => '');
|
||||||
|
if (decAmount.isEmpty) {
|
||||||
|
return numAmount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "$numAmount.$decAmount";
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String getRawAccuracy(String? raw, BigInt rawPerCur) {
|
||||||
|
final String rawString = getRawAsUsableString(raw, rawPerCur);
|
||||||
|
final String rawDecimalString = getRawAsDecimal(raw, rawPerCur).toString();
|
||||||
|
|
||||||
|
if (raw == null || raw.isEmpty || raw == "0") {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rawString != rawDecimalString) {
|
||||||
|
return "~";
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return readable string amount as raw string
|
||||||
|
/// @param amount 1.01
|
||||||
|
/// @returns 101000000000000000000000000000
|
||||||
|
///
|
||||||
|
@override
|
||||||
|
String getAmountAsRaw(String amount, BigInt rawPerCur) {
|
||||||
|
final Decimal asDecimal = Decimal.parse(amount);
|
||||||
|
final Decimal rawDecimal = Decimal.parse(rawPerCur.toString());
|
||||||
|
return (asDecimal * rawDecimal).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<AccountInfoResponse?> getInfoFromSeedOrMnemonic(
|
||||||
|
DerivationType derivationType, {
|
||||||
|
String? seedKey,
|
||||||
|
String? mnemonic,
|
||||||
|
required Node node,
|
||||||
|
}) async {
|
||||||
|
NanoClient nanoClient = NanoClient();
|
||||||
|
nanoClient.connect(node);
|
||||||
|
late String publicAddress;
|
||||||
|
|
||||||
|
if (seedKey != null) {
|
||||||
|
if (derivationType == DerivationType.bip39) {
|
||||||
|
publicAddress = await hdSeedToAddress(seedKey, 0);
|
||||||
|
} else if (derivationType == DerivationType.nano) {
|
||||||
|
publicAddress = await seedToAddress(seedKey, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (derivationType == DerivationType.bip39) {
|
||||||
|
if (mnemonic != null) {
|
||||||
|
seedKey = await hdMnemonicListToSeed(mnemonic.split(' '));
|
||||||
|
publicAddress = await hdSeedToAddress(seedKey, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (derivationType == DerivationType.nano) {
|
||||||
|
if (mnemonic != null) {
|
||||||
|
seedKey = await mnemonicToSeed(mnemonic);
|
||||||
|
publicAddress = await seedToAddress(seedKey, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AccountInfoResponse? accountInfo = await nanoClient.getAccountInfo(publicAddress);
|
||||||
|
if (accountInfo == null) {
|
||||||
|
accountInfo = AccountInfoResponse(frontier: "", balance: "0", representative: "", confirmationHeight: 0);
|
||||||
|
}
|
||||||
|
accountInfo.address = publicAddress;
|
||||||
|
return accountInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<List<DerivationType>> compareDerivationMethods({
|
||||||
|
String? mnemonic,
|
||||||
|
String? privateKey,
|
||||||
|
required Node node,
|
||||||
|
}) async {
|
||||||
|
String? seedKey = privateKey;
|
||||||
|
|
||||||
|
if (mnemonic?.split(' ').length == 12) {
|
||||||
|
return [DerivationType.bip39];
|
||||||
|
}
|
||||||
|
if (seedKey?.length == 128) {
|
||||||
|
return [DerivationType.bip39];
|
||||||
|
} else if (seedKey?.length == 64) {
|
||||||
|
return [DerivationType.nano];
|
||||||
|
}
|
||||||
|
|
||||||
|
late String publicAddressStandard;
|
||||||
|
late String publicAddressBip39;
|
||||||
|
|
||||||
|
try {
|
||||||
|
NanoClient nanoClient = NanoClient();
|
||||||
|
nanoClient.connect(node);
|
||||||
|
|
||||||
|
if (mnemonic != null) {
|
||||||
|
seedKey = await hdMnemonicListToSeed(mnemonic.split(' '));
|
||||||
|
publicAddressBip39 = await hdSeedToAddress(seedKey, 0);
|
||||||
|
|
||||||
|
seedKey = await mnemonicToSeed(mnemonic);
|
||||||
|
publicAddressStandard = await seedToAddress(seedKey, 0);
|
||||||
|
} else if (seedKey != null) {
|
||||||
|
try {
|
||||||
|
publicAddressBip39 = await hdSeedToAddress(seedKey, 0);
|
||||||
|
} catch (e) {
|
||||||
|
return [DerivationType.nano];
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
publicAddressStandard = await seedToAddress(seedKey, 0);
|
||||||
|
} catch (e) {
|
||||||
|
return [DerivationType.bip39];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if account has a history:
|
||||||
|
AccountInfoResponse? bip39Info;
|
||||||
|
AccountInfoResponse? standardInfo;
|
||||||
|
|
||||||
|
try {
|
||||||
|
bip39Info = await nanoClient.getAccountInfo(publicAddressBip39);
|
||||||
|
} catch (e) {
|
||||||
|
bip39Info = null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
standardInfo = await nanoClient.getAccountInfo(publicAddressStandard);
|
||||||
|
} catch (e) {
|
||||||
|
standardInfo = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// one of these is *probably* null:
|
||||||
|
if (bip39Info == null && standardInfo != null) {
|
||||||
|
return [DerivationType.nano];
|
||||||
|
} else if (standardInfo == null && bip39Info != null) {
|
||||||
|
return [DerivationType.bip39];
|
||||||
|
}
|
||||||
|
|
||||||
|
// we don't know for sure:
|
||||||
|
return [DerivationType.nano, DerivationType.bip39];
|
||||||
|
} catch (e) {
|
||||||
|
return [DerivationType.unknown];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,4 +13,11 @@ void startOnCurrentNodeChangeReaction(AppStore appStore) {
|
||||||
print(e.toString());
|
print(e.toString());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
appStore.settingsStore.powNodes.observe((change) async {
|
||||||
|
try {
|
||||||
|
await appStore.wallet!.connectToPowNode(node: change.newValue!);
|
||||||
|
} catch (e) {
|
||||||
|
print(e.toString());
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,16 +58,23 @@ void startCurrentWalletChangeReaction(
|
||||||
}
|
}
|
||||||
|
|
||||||
final node = settingsStore.getCurrentNode(wallet.type);
|
final node = settingsStore.getCurrentNode(wallet.type);
|
||||||
|
|
||||||
startWalletSyncStatusChangeReaction(wallet, fiatConversionStore);
|
startWalletSyncStatusChangeReaction(wallet, fiatConversionStore);
|
||||||
startCheckConnectionReaction(wallet, settingsStore);
|
startCheckConnectionReaction(wallet, settingsStore);
|
||||||
await getIt.get<SharedPreferences>().setString(PreferencesKey.currentWalletName, wallet.name);
|
await getIt.get<SharedPreferences>().setString(PreferencesKey.currentWalletName, wallet.name);
|
||||||
await getIt
|
await getIt
|
||||||
.get<SharedPreferences>()
|
.get<SharedPreferences>()
|
||||||
.setInt(PreferencesKey.currentWalletType, serializeToInt(wallet.type));
|
.setInt(PreferencesKey.currentWalletType, serializeToInt(wallet.type));
|
||||||
|
|
||||||
if (wallet.type == WalletType.monero) {
|
if (wallet.type == WalletType.monero) {
|
||||||
_setAutoGenerateSubaddressStatus(wallet, settingsStore);
|
_setAutoGenerateSubaddressStatus(wallet, settingsStore);
|
||||||
}
|
}
|
||||||
|
|
||||||
await wallet.connectToNode(node: node);
|
await wallet.connectToNode(node: node);
|
||||||
|
if (wallet.type == WalletType.nano || wallet.type == WalletType.banano) {
|
||||||
|
final powNode = settingsStore.getCurrentPowNode(wallet.type);
|
||||||
|
await wallet.connectToPowNode(node: powNode);
|
||||||
|
}
|
||||||
|
|
||||||
if (wallet.type == WalletType.haven) {
|
if (wallet.type == WalletType.haven) {
|
||||||
await updateHavenRate(fiatConversionStore);
|
await updateHavenRate(fiatConversionStore);
|
||||||
|
@ -101,7 +108,7 @@ void startCurrentWalletChangeReaction(
|
||||||
|
|
||||||
if (wallet.type == WalletType.ethereum) {
|
if (wallet.type == WalletType.ethereum) {
|
||||||
final currencies =
|
final currencies =
|
||||||
ethereum!.getERC20Currencies(appStore.wallet!).where((element) => element.enabled);
|
ethereum!.getERC20Currencies(appStore.wallet!).where((element) => element.enabled);
|
||||||
|
|
||||||
for (final currency in currencies) {
|
for (final currency in currencies) {
|
||||||
() async {
|
() async {
|
||||||
|
|
266
lib/router.dart
266
lib/router.dart
|
@ -12,11 +12,15 @@ import 'package:cake_wallet/src/screens/buy/buy_webview_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/buy/webview_page.dart';
|
import 'package:cake_wallet/src/screens/buy/webview_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/dashboard/edit_token_page.dart';
|
import 'package:cake_wallet/src/screens/dashboard/edit_token_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/dashboard/home_settings_page.dart';
|
import 'package:cake_wallet/src/screens/dashboard/home_settings_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/nano/nano_change_rep_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/nano_accounts/nano_account_edit_or_create_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/nodes/pow_node_create_or_edit_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/restore/sweeping_wallet_page.dart';
|
import 'package:cake_wallet/src/screens/restore/sweeping_wallet_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/receive/anonpay_invoice_page.dart';
|
import 'package:cake_wallet/src/screens/receive/anonpay_invoice_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/receive/anonpay_receive_page.dart';
|
import 'package:cake_wallet/src/screens/receive/anonpay_receive_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/dashboard/desktop_widgets/desktop_dashboard_actions.dart';
|
import 'package:cake_wallet/src/screens/dashboard/desktop_widgets/desktop_dashboard_actions.dart';
|
||||||
import 'package:cake_wallet/src/screens/dashboard/widgets/transactions_page.dart';
|
import 'package:cake_wallet/src/screens/dashboard/widgets/transactions_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/restore/wallet_restore_choose_derivation.dart';
|
||||||
import 'package:cake_wallet/src/screens/settings/desktop_settings/desktop_settings_page.dart';
|
import 'package:cake_wallet/src/screens/settings/desktop_settings/desktop_settings_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/settings/display_settings_page.dart';
|
import 'package:cake_wallet/src/screens/settings/display_settings_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/settings/manage_nodes_page.dart';
|
import 'package:cake_wallet/src/screens/settings/manage_nodes_page.dart';
|
||||||
|
@ -51,6 +55,8 @@ import 'package:cake_wallet/view_model/monero_account_list/account_list_item.dar
|
||||||
import 'package:cake_wallet/view_model/node_list/node_create_or_edit_view_model.dart';
|
import 'package:cake_wallet/view_model/node_list/node_create_or_edit_view_model.dart';
|
||||||
import 'package:cake_wallet/view_model/advanced_privacy_settings_view_model.dart';
|
import 'package:cake_wallet/view_model/advanced_privacy_settings_view_model.dart';
|
||||||
import 'package:cake_wallet/wallet_type_utils.dart';
|
import 'package:cake_wallet/wallet_type_utils.dart';
|
||||||
|
import 'package:cw_core/nano_account.dart';
|
||||||
|
import 'package:cw_core/wallet_info.dart';
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:cake_wallet/routes.dart';
|
import 'package:cake_wallet/routes.dart';
|
||||||
|
@ -111,36 +117,34 @@ Route<dynamic> createRoute(RouteSettings settings) {
|
||||||
|
|
||||||
case Routes.newWalletFromWelcome:
|
case Routes.newWalletFromWelcome:
|
||||||
return CupertinoPageRoute<void>(
|
return CupertinoPageRoute<void>(
|
||||||
builder: (_) => getIt.get<SetupPinCodePage>(
|
builder: (_) =>
|
||||||
param1: (PinCodeState<PinCodeWidget> context, dynamic _) {
|
getIt.get<SetupPinCodePage>(param1: (PinCodeState<PinCodeWidget> context, dynamic _) {
|
||||||
if (availableWalletTypes.length == 1) {
|
if (availableWalletTypes.length == 1) {
|
||||||
Navigator.of(context.context).pushNamed(Routes.newWallet, arguments: availableWalletTypes.first);
|
Navigator.of(context.context)
|
||||||
} else {
|
.pushNamed(Routes.newWallet, arguments: availableWalletTypes.first);
|
||||||
Navigator.of(context.context).pushNamed(Routes.newWalletType);
|
} else {
|
||||||
}
|
Navigator.of(context.context).pushNamed(Routes.newWalletType);
|
||||||
}),
|
}
|
||||||
|
}),
|
||||||
fullscreenDialog: true);
|
fullscreenDialog: true);
|
||||||
|
|
||||||
case Routes.newWalletType:
|
case Routes.newWalletType:
|
||||||
return CupertinoPageRoute<void>(
|
return CupertinoPageRoute<void>(
|
||||||
builder: (_) => getIt.get<NewWalletTypePage>(
|
builder: (_) => getIt.get<NewWalletTypePage>(
|
||||||
param1: (BuildContext context, WalletType type) =>
|
param1: (BuildContext context, WalletType type) =>
|
||||||
Navigator.of(context)
|
Navigator.of(context).pushNamed(Routes.newWallet, arguments: type)));
|
||||||
.pushNamed(Routes.newWallet, arguments: type)));
|
|
||||||
|
|
||||||
case Routes.newWallet:
|
case Routes.newWallet:
|
||||||
final type = settings.arguments as WalletType;
|
final type = settings.arguments as WalletType;
|
||||||
final walletNewVM = getIt.get<WalletNewVM>(param1: type);
|
final walletNewVM = getIt.get<WalletNewVM>(param1: type);
|
||||||
|
|
||||||
return CupertinoPageRoute<void>(
|
return CupertinoPageRoute<void>(builder: (_) => NewWalletPage(walletNewVM));
|
||||||
builder: (_) => NewWalletPage(walletNewVM));
|
|
||||||
|
|
||||||
case Routes.setupPin:
|
case Routes.setupPin:
|
||||||
Function(PinCodeState<PinCodeWidget>, String)? callback;
|
Function(PinCodeState<PinCodeWidget>, String)? callback;
|
||||||
|
|
||||||
if (settings.arguments is Function(PinCodeState<PinCodeWidget>, String)) {
|
if (settings.arguments is Function(PinCodeState<PinCodeWidget>, String)) {
|
||||||
callback =
|
callback = settings.arguments as Function(PinCodeState<PinCodeWidget>, String);
|
||||||
settings.arguments as Function(PinCodeState<PinCodeWidget>, String);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return CupertinoPageRoute<void>(
|
return CupertinoPageRoute<void>(
|
||||||
|
@ -150,8 +154,7 @@ Route<dynamic> createRoute(RouteSettings settings) {
|
||||||
return CupertinoPageRoute<void>(
|
return CupertinoPageRoute<void>(
|
||||||
builder: (_) => getIt.get<NewWalletTypePage>(
|
builder: (_) => getIt.get<NewWalletTypePage>(
|
||||||
param1: (BuildContext context, WalletType type) =>
|
param1: (BuildContext context, WalletType type) =>
|
||||||
Navigator.of(context)
|
Navigator.of(context).pushNamed(Routes.restoreWallet, arguments: type),
|
||||||
.pushNamed(Routes.restoreWallet, arguments: type),
|
|
||||||
param2: false));
|
param2: false));
|
||||||
|
|
||||||
case Routes.restoreOptions:
|
case Routes.restoreOptions:
|
||||||
|
@ -166,66 +169,62 @@ Route<dynamic> createRoute(RouteSettings settings) {
|
||||||
if (isNewInstall) {
|
if (isNewInstall) {
|
||||||
return CupertinoPageRoute<void>(
|
return CupertinoPageRoute<void>(
|
||||||
builder: (_) => getIt.get<SetupPinCodePage>(
|
builder: (_) => getIt.get<SetupPinCodePage>(
|
||||||
param1: (PinCodeState<PinCodeWidget> context, dynamic _) {
|
param1: (PinCodeState<PinCodeWidget> context, dynamic _) {
|
||||||
if (isSingleCoin) {
|
if (isSingleCoin) {
|
||||||
return Navigator.of(context.context)
|
return Navigator.of(context.context)
|
||||||
.pushNamed(Routes.restoreWallet, arguments: availableWalletTypes.first);
|
.pushNamed(Routes.restoreWallet, arguments: availableWalletTypes.first);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Navigator.pushNamed(
|
return Navigator.pushNamed(context.context, Routes.restoreWalletType);
|
||||||
context.context, Routes.restoreWalletType);
|
|
||||||
}),
|
}),
|
||||||
fullscreenDialog: true);
|
fullscreenDialog: true);
|
||||||
} else if (isSingleCoin) {
|
} else if (isSingleCoin) {
|
||||||
return MaterialPageRoute<void>(
|
return MaterialPageRoute<void>(
|
||||||
builder: (_) => getIt.get<WalletRestorePage>(
|
builder: (_) => getIt.get<WalletRestorePage>(param1: availableWalletTypes.first));
|
||||||
param1: availableWalletTypes.first
|
|
||||||
));
|
|
||||||
} else {
|
} else {
|
||||||
return CupertinoPageRoute<void>(
|
return CupertinoPageRoute<void>(
|
||||||
builder: (_) => getIt.get<NewWalletTypePage>(
|
builder: (_) => getIt.get<NewWalletTypePage>(
|
||||||
param1: (BuildContext context, WalletType type) =>
|
param1: (BuildContext context, WalletType type) =>
|
||||||
Navigator.of(context)
|
Navigator.of(context).pushNamed(Routes.restoreWallet, arguments: type),
|
||||||
.pushNamed(Routes.restoreWallet, arguments: type),
|
|
||||||
param2: false));
|
param2: false));
|
||||||
}
|
}
|
||||||
|
|
||||||
case Routes.seed:
|
case Routes.seed:
|
||||||
return MaterialPageRoute<void>(
|
return MaterialPageRoute<void>(
|
||||||
fullscreenDialog: true,
|
fullscreenDialog: true,
|
||||||
builder: (_) =>
|
builder: (_) => getIt.get<WalletSeedPage>(param1: settings.arguments as bool));
|
||||||
getIt.get<WalletSeedPage>(param1: settings.arguments as bool));
|
|
||||||
|
|
||||||
case Routes.restoreWallet:
|
case Routes.restoreWallet:
|
||||||
return MaterialPageRoute<void>(
|
return MaterialPageRoute<void>(
|
||||||
builder: (_) => getIt.get<WalletRestorePage>(
|
builder: (_) => getIt.get<WalletRestorePage>(param1: settings.arguments as WalletType));
|
||||||
param1: settings.arguments as WalletType));
|
|
||||||
|
case Routes.restoreWalletChooseDerivation:
|
||||||
|
return MaterialPageRoute<void>(
|
||||||
|
builder: (_) => getIt.get<WalletRestoreChooseDerivationPage>(
|
||||||
|
param1: settings.arguments as List<DerivationInfo>));
|
||||||
|
|
||||||
case Routes.sweepingWalletPage:
|
case Routes.sweepingWalletPage:
|
||||||
return CupertinoPageRoute<void>(
|
return CupertinoPageRoute<void>(builder: (_) => getIt.get<SweepingWalletPage>());
|
||||||
builder: (_) => getIt.get<SweepingWalletPage>());
|
|
||||||
|
|
||||||
case Routes.dashboard:
|
case Routes.dashboard:
|
||||||
return CupertinoPageRoute<void>(
|
return CupertinoPageRoute<void>(
|
||||||
settings: settings,
|
settings: settings, builder: (_) => getIt.get<DashboardPage>());
|
||||||
builder: (_) => getIt.get<DashboardPage>());
|
|
||||||
|
|
||||||
case Routes.send:
|
case Routes.send:
|
||||||
final initialPaymentRequest = settings.arguments as PaymentRequest?;
|
final initialPaymentRequest = settings.arguments as PaymentRequest?;
|
||||||
|
|
||||||
return CupertinoPageRoute<void>(
|
return CupertinoPageRoute<void>(
|
||||||
fullscreenDialog: true, builder: (_) => getIt.get<SendPage>(
|
fullscreenDialog: true,
|
||||||
param1: initialPaymentRequest,
|
builder: (_) => getIt.get<SendPage>(
|
||||||
));
|
param1: initialPaymentRequest,
|
||||||
|
));
|
||||||
|
|
||||||
case Routes.sendTemplate:
|
case Routes.sendTemplate:
|
||||||
return CupertinoPageRoute<void>(
|
return CupertinoPageRoute<void>(
|
||||||
fullscreenDialog: true,
|
fullscreenDialog: true, builder: (_) => getIt.get<SendTemplatePage>());
|
||||||
builder: (_) => getIt.get<SendTemplatePage>());
|
|
||||||
|
|
||||||
case Routes.receive:
|
case Routes.receive:
|
||||||
return CupertinoPageRoute<void>(
|
return CupertinoPageRoute<void>(builder: (_) => getIt.get<ReceivePage>());
|
||||||
builder: (_) => getIt.get<ReceivePage>());
|
|
||||||
|
|
||||||
case Routes.addressPage:
|
case Routes.addressPage:
|
||||||
return CupertinoPageRoute<void>(
|
return CupertinoPageRoute<void>(
|
||||||
|
@ -234,20 +233,21 @@ Route<dynamic> createRoute(RouteSettings settings) {
|
||||||
case Routes.transactionDetails:
|
case Routes.transactionDetails:
|
||||||
return CupertinoPageRoute<void>(
|
return CupertinoPageRoute<void>(
|
||||||
fullscreenDialog: true,
|
fullscreenDialog: true,
|
||||||
builder: (_) => getIt.get<TransactionDetailsPage>(
|
builder: (_) =>
|
||||||
param1: settings.arguments as TransactionInfo));
|
getIt.get<TransactionDetailsPage>(param1: settings.arguments as TransactionInfo));
|
||||||
|
|
||||||
case Routes.newSubaddress:
|
case Routes.newSubaddress:
|
||||||
return CupertinoPageRoute<void>(
|
return CupertinoPageRoute<void>(
|
||||||
builder: (_) =>
|
builder: (_) => getIt.get<AddressEditOrCreatePage>(param1: settings.arguments));
|
||||||
getIt.get<AddressEditOrCreatePage>(param1: settings.arguments));
|
|
||||||
|
|
||||||
case Routes.disclaimer:
|
case Routes.disclaimer:
|
||||||
return CupertinoPageRoute<void>(builder: (_) => DisclaimerPage());
|
return CupertinoPageRoute<void>(builder: (_) => DisclaimerPage());
|
||||||
|
|
||||||
case Routes.readDisclaimer:
|
case Routes.readDisclaimer:
|
||||||
return CupertinoPageRoute<void>(
|
return CupertinoPageRoute<void>(builder: (_) => DisclaimerPage(isReadOnly: true));
|
||||||
builder: (_) => DisclaimerPage(isReadOnly: true));
|
|
||||||
|
case Routes.changeRep:
|
||||||
|
return CupertinoPageRoute<void>(builder: (_) => getIt.get<NanoChangeRepPage>());
|
||||||
|
|
||||||
case Routes.seedLanguage:
|
case Routes.seedLanguage:
|
||||||
final args = settings.arguments as List<dynamic>;
|
final args = settings.arguments as List<dynamic>;
|
||||||
|
@ -256,8 +256,8 @@ Route<dynamic> createRoute(RouteSettings settings) {
|
||||||
|
|
||||||
return CupertinoPageRoute<void>(builder: (_) {
|
return CupertinoPageRoute<void>(builder: (_) {
|
||||||
return SeedLanguage(
|
return SeedLanguage(
|
||||||
onConfirm: (context, lang) => Navigator.of(context)
|
onConfirm: (context, lang) =>
|
||||||
.popAndPushNamed(redirectRoute, arguments: [type, lang]));
|
Navigator.of(context).popAndPushNamed(redirectRoute, arguments: [type, lang]));
|
||||||
});
|
});
|
||||||
|
|
||||||
case Routes.walletList:
|
case Routes.walletList:
|
||||||
|
@ -267,15 +267,13 @@ Route<dynamic> createRoute(RouteSettings settings) {
|
||||||
case Routes.walletEdit:
|
case Routes.walletEdit:
|
||||||
return MaterialPageRoute<void>(
|
return MaterialPageRoute<void>(
|
||||||
fullscreenDialog: true,
|
fullscreenDialog: true,
|
||||||
builder: (_) => getIt.get<WalletEditPage>(
|
builder: (_) => getIt.get<WalletEditPage>(param1: settings.arguments as List<dynamic>));
|
||||||
param1: settings.arguments as List<dynamic>));
|
|
||||||
|
|
||||||
case Routes.auth:
|
case Routes.auth:
|
||||||
return MaterialPageRoute<void>(
|
return MaterialPageRoute<void>(
|
||||||
fullscreenDialog: true,
|
fullscreenDialog: true,
|
||||||
builder: (_) => getIt.get<AuthPage>(
|
builder: (_) => getIt.get<AuthPage>(
|
||||||
param1: settings.arguments as OnAuthenticationFinished,
|
param1: settings.arguments as OnAuthenticationFinished, param2: true));
|
||||||
param2: true));
|
|
||||||
|
|
||||||
case Routes.totpAuthCodePage:
|
case Routes.totpAuthCodePage:
|
||||||
final args = settings.arguments as TotpAuthArgumentsModel;
|
final args = settings.arguments as TotpAuthArgumentsModel;
|
||||||
|
@ -290,9 +288,9 @@ Route<dynamic> createRoute(RouteSettings settings) {
|
||||||
return CupertinoPageRoute<void>(
|
return CupertinoPageRoute<void>(
|
||||||
builder: (context) => WillPopScope(
|
builder: (context) => WillPopScope(
|
||||||
child: getIt.get<AuthPage>(instanceName: 'login'),
|
child: getIt.get<AuthPage>(instanceName: 'login'),
|
||||||
onWillPop: () async =>
|
onWillPop: () async =>
|
||||||
// FIX-ME: Additional check does it works correctly
|
// FIX-ME: Additional check does it works correctly
|
||||||
(await SystemChannels.platform.invokeMethod<bool>('SystemNavigator.pop') ??
|
(await SystemChannels.platform.invokeMethod<bool>('SystemNavigator.pop') ??
|
||||||
false),
|
false),
|
||||||
),
|
),
|
||||||
fullscreenDialog: true);
|
fullscreenDialog: true);
|
||||||
|
@ -302,53 +300,54 @@ Route<dynamic> createRoute(RouteSettings settings) {
|
||||||
fullscreenDialog: true,
|
fullscreenDialog: true,
|
||||||
builder: (_) => WillPopScope(
|
builder: (_) => WillPopScope(
|
||||||
child: getIt.get<AuthPage>(
|
child: getIt.get<AuthPage>(
|
||||||
param1: settings.arguments as OnAuthenticationFinished,
|
param1: settings.arguments as OnAuthenticationFinished, param2: false),
|
||||||
param2: false),
|
|
||||||
onWillPop: () async => false));
|
onWillPop: () async => false));
|
||||||
|
|
||||||
case Routes.connectionSync:
|
case Routes.connectionSync:
|
||||||
return CupertinoPageRoute<void>(
|
return CupertinoPageRoute<void>(
|
||||||
fullscreenDialog: true,
|
fullscreenDialog: true, builder: (_) => getIt.get<ConnectionSyncPage>());
|
||||||
builder: (_) => getIt.get<ConnectionSyncPage>());
|
|
||||||
|
|
||||||
case Routes.securityBackupPage:
|
case Routes.securityBackupPage:
|
||||||
return CupertinoPageRoute<void>(
|
return CupertinoPageRoute<void>(
|
||||||
fullscreenDialog: true,
|
fullscreenDialog: true, builder: (_) => getIt.get<SecurityBackupPage>());
|
||||||
builder: (_) => getIt.get<SecurityBackupPage>());
|
|
||||||
|
|
||||||
case Routes.privacyPage:
|
case Routes.privacyPage:
|
||||||
return CupertinoPageRoute<void>(
|
return CupertinoPageRoute<void>(
|
||||||
fullscreenDialog: true,
|
fullscreenDialog: true, builder: (_) => getIt.get<PrivacyPage>());
|
||||||
builder: (_) => getIt.get<PrivacyPage>());
|
|
||||||
|
|
||||||
case Routes.displaySettingsPage:
|
case Routes.displaySettingsPage:
|
||||||
return CupertinoPageRoute<void>(
|
return CupertinoPageRoute<void>(
|
||||||
fullscreenDialog: true,
|
fullscreenDialog: true, builder: (_) => getIt.get<DisplaySettingsPage>());
|
||||||
builder: (_) => getIt.get<DisplaySettingsPage>());
|
|
||||||
|
|
||||||
case Routes.otherSettingsPage:
|
case Routes.otherSettingsPage:
|
||||||
return CupertinoPageRoute<void>(
|
return CupertinoPageRoute<void>(
|
||||||
fullscreenDialog: true,
|
fullscreenDialog: true, builder: (_) => getIt.get<OtherSettingsPage>());
|
||||||
builder: (_) => getIt.get<OtherSettingsPage>());
|
|
||||||
|
|
||||||
case Routes.newNode:
|
case Routes.newNode:
|
||||||
final args = settings.arguments as Map<String, dynamic>?;
|
final args = settings.arguments as Map<String, dynamic>?;
|
||||||
return CupertinoPageRoute<void>(
|
return CupertinoPageRoute<void>(
|
||||||
builder: (_) => getIt.get<NodeCreateOrEditPage>(
|
builder: (_) => getIt.get<NodeCreateOrEditPage>(
|
||||||
param1: args?['editingNode'] as Node?,
|
param1: args?['editingNode'] as Node?, param2: args?['isSelected'] as bool?));
|
||||||
param2: args?['isSelected'] as bool?));
|
|
||||||
|
|
||||||
|
|
||||||
|
case Routes.newPowNode:
|
||||||
|
final args = settings.arguments as Map<String, dynamic>?;
|
||||||
|
return CupertinoPageRoute<void>(
|
||||||
|
builder: (_) => getIt.get<PowNodeCreateOrEditPage>(
|
||||||
|
param1: args?['editingNode'] as Node?, param2: args?['isSelected'] as bool?));
|
||||||
|
|
||||||
case Routes.accountCreation:
|
case Routes.accountCreation:
|
||||||
return CupertinoPageRoute<String>(
|
return CupertinoPageRoute<String>(
|
||||||
builder: (_) => getIt.get<MoneroAccountEditOrCreatePage>(
|
builder: (_) => getIt.get<MoneroAccountEditOrCreatePage>(
|
||||||
param1: settings.arguments as AccountListItem?));
|
param1: settings.arguments as AccountListItem?));
|
||||||
|
|
||||||
|
case Routes.nanoAccountCreation:
|
||||||
|
return CupertinoPageRoute<String>(
|
||||||
|
builder: (_) =>
|
||||||
|
getIt.get<NanoAccountEditOrCreatePage>(param1: settings.arguments as NanoAccount?));
|
||||||
|
|
||||||
case Routes.addressBook:
|
case Routes.addressBook:
|
||||||
return MaterialPageRoute<void>(
|
return MaterialPageRoute<void>(
|
||||||
fullscreenDialog: true,
|
fullscreenDialog: true, builder: (_) => getIt.get<ContactListPage>());
|
||||||
builder: (_) => getIt.get<ContactListPage>());
|
|
||||||
|
|
||||||
case Routes.pickerAddressBook:
|
case Routes.pickerAddressBook:
|
||||||
final selectedCurrency = settings.arguments as CryptoCurrency?;
|
final selectedCurrency = settings.arguments as CryptoCurrency?;
|
||||||
|
@ -357,31 +356,26 @@ Route<dynamic> createRoute(RouteSettings settings) {
|
||||||
|
|
||||||
case Routes.addressBookAddContact:
|
case Routes.addressBookAddContact:
|
||||||
return CupertinoPageRoute<void>(
|
return CupertinoPageRoute<void>(
|
||||||
builder: (_) => getIt.get<ContactPage>(
|
builder: (_) => getIt.get<ContactPage>(param1: settings.arguments as ContactRecord?));
|
||||||
param1: settings.arguments as ContactRecord?));
|
|
||||||
|
|
||||||
case Routes.showKeys:
|
case Routes.showKeys:
|
||||||
return MaterialPageRoute<void>(
|
return MaterialPageRoute<void>(
|
||||||
builder: (_) => getIt.get<WalletKeysPage>(), fullscreenDialog: true);
|
builder: (_) => getIt.get<WalletKeysPage>(), fullscreenDialog: true);
|
||||||
|
|
||||||
case Routes.exchangeTrade:
|
case Routes.exchangeTrade:
|
||||||
return CupertinoPageRoute<void>(
|
return CupertinoPageRoute<void>(builder: (_) => getIt.get<ExchangeTradePage>());
|
||||||
builder: (_) => getIt.get<ExchangeTradePage>());
|
|
||||||
|
|
||||||
case Routes.exchangeConfirm:
|
case Routes.exchangeConfirm:
|
||||||
return MaterialPageRoute<void>(
|
return MaterialPageRoute<void>(builder: (_) => getIt.get<ExchangeConfirmPage>());
|
||||||
builder: (_) => getIt.get<ExchangeConfirmPage>());
|
|
||||||
|
|
||||||
case Routes.tradeDetails:
|
case Routes.tradeDetails:
|
||||||
return MaterialPageRoute<void>(
|
return MaterialPageRoute<void>(
|
||||||
fullscreenDialog: true,
|
fullscreenDialog: true,
|
||||||
builder: (_) =>
|
builder: (_) => getIt.get<TradeDetailsPage>(param1: settings.arguments as Trade));
|
||||||
getIt.get<TradeDetailsPage>(param1: settings.arguments as Trade));
|
|
||||||
|
|
||||||
case Routes.orderDetails:
|
case Routes.orderDetails:
|
||||||
return MaterialPageRoute<void>(
|
return MaterialPageRoute<void>(
|
||||||
builder: (_) =>
|
builder: (_) => getIt.get<OrderDetailsPage>(param1: settings.arguments as Order));
|
||||||
getIt.get<OrderDetailsPage>(param1: settings.arguments as Order));
|
|
||||||
|
|
||||||
case Routes.buy:
|
case Routes.buy:
|
||||||
return MaterialPageRoute<void>(builder: (_) => getIt.get<BuyOptionsPage>());
|
return MaterialPageRoute<void>(builder: (_) => getIt.get<BuyOptionsPage>());
|
||||||
|
@ -390,18 +384,14 @@ Route<dynamic> createRoute(RouteSettings settings) {
|
||||||
final args = settings.arguments as List;
|
final args = settings.arguments as List;
|
||||||
|
|
||||||
return MaterialPageRoute<void>(
|
return MaterialPageRoute<void>(
|
||||||
fullscreenDialog: true,
|
fullscreenDialog: true, builder: (_) => getIt.get<BuyWebViewPage>(param1: args));
|
||||||
builder: (_) =>
|
|
||||||
getIt.get<BuyWebViewPage>(param1: args));
|
|
||||||
|
|
||||||
case Routes.exchange:
|
case Routes.exchange:
|
||||||
return CupertinoPageRoute<void>(
|
return CupertinoPageRoute<void>(
|
||||||
fullscreenDialog: true,
|
fullscreenDialog: true, builder: (_) => getIt.get<ExchangePage>());
|
||||||
builder: (_) => getIt.get<ExchangePage>());
|
|
||||||
|
|
||||||
case Routes.exchangeTemplate:
|
case Routes.exchangeTemplate:
|
||||||
return CupertinoPageRoute<void>(
|
return CupertinoPageRoute<void>(builder: (_) => getIt.get<ExchangeTemplatePage>());
|
||||||
builder: (_) => getIt.get<ExchangeTemplatePage>());
|
|
||||||
|
|
||||||
case Routes.rescan:
|
case Routes.rescan:
|
||||||
return MaterialPageRoute<void>(builder: (_) => getIt.get<RescanPage>());
|
return MaterialPageRoute<void>(builder: (_) => getIt.get<RescanPage>());
|
||||||
|
@ -411,51 +401,41 @@ Route<dynamic> createRoute(RouteSettings settings) {
|
||||||
|
|
||||||
case Routes.preSeed:
|
case Routes.preSeed:
|
||||||
return MaterialPageRoute<void>(
|
return MaterialPageRoute<void>(
|
||||||
builder: (_) =>
|
builder: (_) => getIt.get<PreSeedPage>(param1: settings.arguments as WalletType));
|
||||||
getIt.get<PreSeedPage>(param1: settings.arguments as WalletType));
|
|
||||||
|
|
||||||
case Routes.backup:
|
case Routes.backup:
|
||||||
return CupertinoPageRoute<void>(
|
return CupertinoPageRoute<void>(
|
||||||
fullscreenDialog: true, builder: (_) => getIt.get<BackupPage>());
|
fullscreenDialog: true, builder: (_) => getIt.get<BackupPage>());
|
||||||
|
|
||||||
case Routes.editBackupPassword:
|
case Routes.editBackupPassword:
|
||||||
return CupertinoPageRoute<void>(
|
return CupertinoPageRoute<void>(builder: (_) => getIt.get<EditBackupPasswordPage>());
|
||||||
builder: (_) => getIt.get<EditBackupPasswordPage>());
|
|
||||||
|
|
||||||
case Routes.restoreFromBackup:
|
case Routes.restoreFromBackup:
|
||||||
return CupertinoPageRoute<void>(
|
return CupertinoPageRoute<void>(
|
||||||
fullscreenDialog: true,
|
fullscreenDialog: true, builder: (_) => getIt.get<RestoreFromBackupPage>());
|
||||||
builder: (_) => getIt.get<RestoreFromBackupPage>());
|
|
||||||
|
|
||||||
case Routes.support:
|
case Routes.support:
|
||||||
return CupertinoPageRoute<void>(
|
return CupertinoPageRoute<void>(builder: (_) => getIt.get<SupportPage>());
|
||||||
builder: (_) => getIt.get<SupportPage>());
|
|
||||||
|
|
||||||
case Routes.supportLiveChat:
|
case Routes.supportLiveChat:
|
||||||
return CupertinoPageRoute<void>(
|
return CupertinoPageRoute<void>(builder: (_) => getIt.get<SupportChatPage>());
|
||||||
builder: (_) => getIt.get<SupportChatPage>());
|
|
||||||
|
|
||||||
case Routes.supportOtherLinks:
|
case Routes.supportOtherLinks:
|
||||||
return CupertinoPageRoute<void>(
|
return CupertinoPageRoute<void>(
|
||||||
fullscreenDialog: true,
|
fullscreenDialog: true, builder: (_) => getIt.get<SupportOtherLinksPage>());
|
||||||
builder: (_) => getIt.get<SupportOtherLinksPage>());
|
|
||||||
|
|
||||||
case Routes.unspentCoinsList:
|
case Routes.unspentCoinsList:
|
||||||
return MaterialPageRoute<void>(
|
return MaterialPageRoute<void>(builder: (_) => getIt.get<UnspentCoinsListPage>());
|
||||||
builder: (_) => getIt.get<UnspentCoinsListPage>());
|
|
||||||
|
|
||||||
case Routes.unspentCoinsDetails:
|
case Routes.unspentCoinsDetails:
|
||||||
final args = settings.arguments as List;
|
final args = settings.arguments as List;
|
||||||
|
|
||||||
return MaterialPageRoute<void>(
|
return MaterialPageRoute<void>(
|
||||||
builder: (_) =>
|
builder: (_) => getIt.get<UnspentCoinsDetailsPage>(param1: args));
|
||||||
getIt.get<UnspentCoinsDetailsPage>(
|
|
||||||
param1: args));
|
|
||||||
|
|
||||||
case Routes.fullscreenQR:
|
case Routes.fullscreenQR:
|
||||||
return MaterialPageRoute<void>(
|
return MaterialPageRoute<void>(
|
||||||
builder: (_) =>
|
builder: (_) => getIt.get<FullscreenQRPage>(
|
||||||
getIt.get<FullscreenQRPage>(
|
|
||||||
param1: settings.arguments as QrViewData,
|
param1: settings.arguments as QrViewData,
|
||||||
));
|
));
|
||||||
|
|
||||||
|
@ -466,26 +446,27 @@ Route<dynamic> createRoute(RouteSettings settings) {
|
||||||
);
|
);
|
||||||
|
|
||||||
case Routes.ioniaLoginPage:
|
case Routes.ioniaLoginPage:
|
||||||
return CupertinoPageRoute<void>( builder: (_) => getIt.get<IoniaLoginPage>());
|
return CupertinoPageRoute<void>(builder: (_) => getIt.get<IoniaLoginPage>());
|
||||||
|
|
||||||
case Routes.ioniaCreateAccountPage:
|
case Routes.ioniaCreateAccountPage:
|
||||||
return CupertinoPageRoute<void>( builder: (_) => getIt.get<IoniaCreateAccountPage>());
|
return CupertinoPageRoute<void>(builder: (_) => getIt.get<IoniaCreateAccountPage>());
|
||||||
|
|
||||||
case Routes.ioniaManageCardsPage:
|
case Routes.ioniaManageCardsPage:
|
||||||
return CupertinoPageRoute<void>(
|
return CupertinoPageRoute<void>(builder: (_) => getIt.get<IoniaManageCardsPage>());
|
||||||
builder: (_) => getIt.get<IoniaManageCardsPage>());
|
|
||||||
|
|
||||||
case Routes.ioniaBuyGiftCardPage:
|
case Routes.ioniaBuyGiftCardPage:
|
||||||
final args = settings.arguments as List;
|
final args = settings.arguments as List;
|
||||||
return CupertinoPageRoute<void>(builder: (_) => getIt.get<IoniaBuyGiftCardPage>(param1: args));
|
return CupertinoPageRoute<void>(
|
||||||
|
builder: (_) => getIt.get<IoniaBuyGiftCardPage>(param1: args));
|
||||||
|
|
||||||
case Routes.ioniaBuyGiftCardDetailPage:
|
case Routes.ioniaBuyGiftCardDetailPage:
|
||||||
final args = settings.arguments as List;
|
final args = settings.arguments as List;
|
||||||
return CupertinoPageRoute<void>(builder: (_) => getIt.get<IoniaBuyGiftCardDetailPage>(param1: args));
|
return CupertinoPageRoute<void>(
|
||||||
|
builder: (_) => getIt.get<IoniaBuyGiftCardDetailPage>(param1: args));
|
||||||
|
|
||||||
case Routes.ioniaVerifyIoniaOtpPage:
|
case Routes.ioniaVerifyIoniaOtpPage:
|
||||||
final args = settings.arguments as List;
|
final args = settings.arguments as List;
|
||||||
return CupertinoPageRoute<void>(builder: (_) =>getIt.get<IoniaVerifyIoniaOtp>(param1: args));
|
return CupertinoPageRoute<void>(builder: (_) => getIt.get<IoniaVerifyIoniaOtp>(param1: args));
|
||||||
|
|
||||||
case Routes.ioniaDebitCardPage:
|
case Routes.ioniaDebitCardPage:
|
||||||
return CupertinoPageRoute<void>(builder: (_) => getIt.get<IoniaDebitCardPage>());
|
return CupertinoPageRoute<void>(builder: (_) => getIt.get<IoniaDebitCardPage>());
|
||||||
|
@ -501,57 +482,60 @@ Route<dynamic> createRoute(RouteSettings settings) {
|
||||||
|
|
||||||
case Routes.ioniaCustomTipPage:
|
case Routes.ioniaCustomTipPage:
|
||||||
final args = settings.arguments as List;
|
final args = settings.arguments as List;
|
||||||
return CupertinoPageRoute<void>(builder: (_) =>getIt.get<IoniaCustomTipPage>(param1: args));
|
return CupertinoPageRoute<void>(builder: (_) => getIt.get<IoniaCustomTipPage>(param1: args));
|
||||||
|
|
||||||
case Routes.ioniaGiftCardDetailPage:
|
case Routes.ioniaGiftCardDetailPage:
|
||||||
final args = settings.arguments as List;
|
final args = settings.arguments as List;
|
||||||
return CupertinoPageRoute<void>(builder: (_) => getIt.get<IoniaGiftCardDetailPage>(param1: args.first));
|
return CupertinoPageRoute<void>(
|
||||||
|
builder: (_) => getIt.get<IoniaGiftCardDetailPage>(param1: args.first));
|
||||||
|
|
||||||
case Routes.ioniaCustomRedeemPage:
|
case Routes.ioniaCustomRedeemPage:
|
||||||
final args = settings.arguments as List;
|
final args = settings.arguments as List;
|
||||||
return CupertinoPageRoute<void>(builder: (_) => getIt.get<IoniaCustomRedeemPage>(param1: args));
|
return CupertinoPageRoute<void>(
|
||||||
|
builder: (_) => getIt.get<IoniaCustomRedeemPage>(param1: args));
|
||||||
|
|
||||||
case Routes.ioniaMoreOptionsPage:
|
case Routes.ioniaMoreOptionsPage:
|
||||||
final args = settings.arguments as List;
|
final args = settings.arguments as List;
|
||||||
return CupertinoPageRoute<void>(builder: (_) => getIt.get<IoniaMoreOptionsPage>(param1: args));
|
return CupertinoPageRoute<void>(
|
||||||
|
builder: (_) => getIt.get<IoniaMoreOptionsPage>(param1: args));
|
||||||
|
|
||||||
case Routes.ioniaPaymentStatusPage:
|
case Routes.ioniaPaymentStatusPage:
|
||||||
final args = settings.arguments as List;
|
final args = settings.arguments as List;
|
||||||
final paymentInfo = args.first as IoniaAnyPayPaymentInfo;
|
final paymentInfo = args.first as IoniaAnyPayPaymentInfo;
|
||||||
final commitedInfo = args[1] as AnyPayPaymentCommittedInfo;
|
final commitedInfo = args[1] as AnyPayPaymentCommittedInfo;
|
||||||
return CupertinoPageRoute<void>(builder: (_) => getIt.get<IoniaPaymentStatusPage>(
|
return CupertinoPageRoute<void>(
|
||||||
param1: paymentInfo,
|
builder: (_) =>
|
||||||
param2: commitedInfo));
|
getIt.get<IoniaPaymentStatusPage>(param1: paymentInfo, param2: commitedInfo));
|
||||||
|
|
||||||
case Routes.webViewPage:
|
case Routes.webViewPage:
|
||||||
final args = settings.arguments as List;
|
final args = settings.arguments as List;
|
||||||
final title = args.first as String;
|
final title = args.first as String;
|
||||||
final url = args[1] as Uri;
|
final url = args[1] as Uri;
|
||||||
return CupertinoPageRoute<void>(builder: (_) => getIt.get<WebViewPage>(
|
return CupertinoPageRoute<void>(
|
||||||
param1: title,
|
builder: (_) => getIt.get<WebViewPage>(param1: title, param2: url));
|
||||||
param2: url));
|
|
||||||
|
|
||||||
case Routes.advancedPrivacySettings:
|
case Routes.advancedPrivacySettings:
|
||||||
final type = settings.arguments as WalletType;
|
final type = settings.arguments as WalletType;
|
||||||
|
|
||||||
return CupertinoPageRoute<void>(
|
return CupertinoPageRoute<void>(
|
||||||
builder: (_) => AdvancedPrivacySettingsPage(
|
builder: (_) => AdvancedPrivacySettingsPage(
|
||||||
getIt.get<AdvancedPrivacySettingsViewModel>(param1: type),
|
getIt.get<AdvancedPrivacySettingsViewModel>(param1: type),
|
||||||
getIt.get<NodeCreateOrEditViewModel>(param1: type),
|
getIt.get<NodeCreateOrEditViewModel>(param1: type, param2: false),
|
||||||
));
|
));
|
||||||
|
|
||||||
case Routes.anonPayInvoicePage:
|
case Routes.anonPayInvoicePage:
|
||||||
final args = settings.arguments as List;
|
final args = settings.arguments as List;
|
||||||
return CupertinoPageRoute<void>(
|
return CupertinoPageRoute<void>(builder: (_) => getIt.get<AnonPayInvoicePage>(param1: args));
|
||||||
builder: (_) => getIt.get<AnonPayInvoicePage>(param1: args));
|
|
||||||
|
|
||||||
case Routes.anonPayReceivePage:
|
case Routes.anonPayReceivePage:
|
||||||
final anonInvoiceViewData = settings.arguments as AnonpayInfoBase;
|
final anonInvoiceViewData = settings.arguments as AnonpayInfoBase;
|
||||||
return CupertinoPageRoute<void>(builder: (_) => getIt.get<AnonPayReceivePage>(param1: anonInvoiceViewData));
|
return CupertinoPageRoute<void>(
|
||||||
|
builder: (_) => getIt.get<AnonPayReceivePage>(param1: anonInvoiceViewData));
|
||||||
|
|
||||||
case Routes.anonPayDetailsPage:
|
case Routes.anonPayDetailsPage:
|
||||||
final anonInvoiceViewData = settings.arguments as AnonpayInvoiceInfo;
|
final anonInvoiceViewData = settings.arguments as AnonpayInvoiceInfo;
|
||||||
return CupertinoPageRoute<void>(builder: (_) => getIt.get<AnonpayDetailsPage>(param1: anonInvoiceViewData));
|
return CupertinoPageRoute<void>(
|
||||||
|
builder: (_) => getIt.get<AnonpayDetailsPage>(param1: anonInvoiceViewData));
|
||||||
|
|
||||||
case Routes.desktop_actions:
|
case Routes.desktop_actions:
|
||||||
return PageRouteBuilder(
|
return PageRouteBuilder(
|
||||||
|
@ -560,12 +544,10 @@ Route<dynamic> createRoute(RouteSettings settings) {
|
||||||
);
|
);
|
||||||
|
|
||||||
case Routes.desktop_settings_page:
|
case Routes.desktop_settings_page:
|
||||||
return CupertinoPageRoute<void>(
|
return CupertinoPageRoute<void>(builder: (_) => DesktopSettingsPage());
|
||||||
builder: (_) => DesktopSettingsPage());
|
|
||||||
|
|
||||||
case Routes.empty_no_route:
|
case Routes.empty_no_route:
|
||||||
return MaterialPageRoute<void>(
|
return MaterialPageRoute<void>(builder: (_) => SizedBox.shrink());
|
||||||
builder: (_) => SizedBox.shrink());
|
|
||||||
|
|
||||||
case Routes.transactionsPage:
|
case Routes.transactionsPage:
|
||||||
return CupertinoPageRoute<void>(
|
return CupertinoPageRoute<void>(
|
||||||
|
@ -602,12 +584,14 @@ Route<dynamic> createRoute(RouteSettings settings) {
|
||||||
);
|
);
|
||||||
|
|
||||||
case Routes.manageNodes:
|
case Routes.manageNodes:
|
||||||
return MaterialPageRoute<void>(builder: (_) => getIt.get<ManageNodesPage>());
|
return MaterialPageRoute<void>(builder: (_) => getIt.get<ManageNodesPage>(param1: false));
|
||||||
|
|
||||||
|
case Routes.managePowNodes:
|
||||||
|
return MaterialPageRoute<void>(builder: (_) => getIt.get<ManageNodesPage>(param1: true));
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return MaterialPageRoute<void>(
|
return MaterialPageRoute<void>(
|
||||||
builder: (_) => Scaffold(
|
builder: (_) => Scaffold(
|
||||||
body: Center(
|
body: Center(child: Text(S.current.router_no_route(settings.name ?? 'No route')))));
|
||||||
child: Text(S.current.router_no_route(settings.name ?? 'No route')))));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ class Routes {
|
||||||
static const seed = '/seed';
|
static const seed = '/seed';
|
||||||
static const restoreOptions = '/restore_options';
|
static const restoreOptions = '/restore_options';
|
||||||
static const restoreWalletFromSeedKeys = '/restore_wallet_from_seeds_keys';
|
static const restoreWalletFromSeedKeys = '/restore_wallet_from_seeds_keys';
|
||||||
|
static const restoreWalletChooseDerivation = '/restore_wallet_choose_derivation';
|
||||||
static const dashboard = '/dashboard';
|
static const dashboard = '/dashboard';
|
||||||
static const send = '/send';
|
static const send = '/send';
|
||||||
static const transactionDetails = '/transaction_info';
|
static const transactionDetails = '/transaction_info';
|
||||||
|
@ -14,13 +15,16 @@ class Routes {
|
||||||
static const walletEdit = '/walletEdit';
|
static const walletEdit = '/walletEdit';
|
||||||
static const disclaimer = '/disclaimer';
|
static const disclaimer = '/disclaimer';
|
||||||
static const readDisclaimer = '/read_disclaimer';
|
static const readDisclaimer = '/read_disclaimer';
|
||||||
|
static const changeRep = '/change_representative';
|
||||||
static const seedLanguage = '/seed_language';
|
static const seedLanguage = '/seed_language';
|
||||||
static const walletList = '/view_model.wallet_list';
|
static const walletList = '/view_model.wallet_list';
|
||||||
static const auth = '/auth';
|
static const auth = '/auth';
|
||||||
static const newNode = '/new_node_list';
|
static const newNode = '/new_node_list';
|
||||||
|
static const newPowNode = '/new_pow_node_list';
|
||||||
static const login = '/login';
|
static const login = '/login';
|
||||||
static const splash = '/splash';
|
static const splash = '/splash';
|
||||||
static const accountCreation = '/account_new';
|
static const accountCreation = '/account_new';
|
||||||
|
static const nanoAccountCreation = '/nano_account_new';
|
||||||
static const addressBook = '/address_book';
|
static const addressBook = '/address_book';
|
||||||
static const pickerAddressBook = '/picker_address_book';
|
static const pickerAddressBook = '/picker_address_book';
|
||||||
static const addressBookAddContact = '/address_book_add_contact';
|
static const addressBookAddContact = '/address_book_add_contact';
|
||||||
|
@ -92,4 +96,6 @@ class Routes {
|
||||||
static const homeSettings = '/home_settings';
|
static const homeSettings = '/home_settings';
|
||||||
static const editToken = '/edit_token';
|
static const editToken = '/edit_token';
|
||||||
static const manageNodes = '/manage_nodes';
|
static const manageNodes = '/manage_nodes';
|
||||||
|
static const managePowNodes = '/manage_pow_nodes';
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -295,29 +295,6 @@ class _DashboardPageView extends BasePage {
|
||||||
);
|
);
|
||||||
_isEffectsInstalled = true;
|
_isEffectsInstalled = true;
|
||||||
|
|
||||||
autorun(
|
|
||||||
(_) async {
|
|
||||||
if (!dashboardViewModel.isOutdatedElectrumWallet) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await Future<void>.delayed(Duration(seconds: 1));
|
|
||||||
if (context.mounted) {
|
|
||||||
await showPopUp<void>(
|
|
||||||
context: context,
|
|
||||||
builder: (BuildContext context) {
|
|
||||||
return AlertWithOneAction(
|
|
||||||
alertTitle: S.of(context).pre_seed_title,
|
|
||||||
alertContent: S.of(context).outdated_electrum_wallet_description,
|
|
||||||
buttonText: S.of(context).understand,
|
|
||||||
buttonAction: () => Navigator.of(context).pop(),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
_showReleaseNotesPopup(context);
|
_showReleaseNotesPopup(context);
|
||||||
|
|
||||||
var needToPresentYat = false;
|
var needToPresentYat = false;
|
||||||
|
|
|
@ -78,23 +78,6 @@ class DesktopDashboardPage extends StatelessWidget {
|
||||||
}
|
}
|
||||||
_isEffectsInstalled = true;
|
_isEffectsInstalled = true;
|
||||||
|
|
||||||
autorun((_) async {
|
|
||||||
if (!dashboardViewModel.isOutdatedElectrumWallet) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await Future<void>.delayed(Duration(seconds: 1));
|
|
||||||
await showPopUp<void>(
|
|
||||||
context: context,
|
|
||||||
builder: (BuildContext context) {
|
|
||||||
return AlertWithOneAction(
|
|
||||||
alertTitle: S.of(context).pre_seed_title,
|
|
||||||
alertContent: S.of(context).outdated_electrum_wallet_description,
|
|
||||||
buttonText: S.of(context).understand,
|
|
||||||
buttonAction: () => Navigator.of(context).pop());
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
var needToPresentYat = false;
|
var needToPresentYat = false;
|
||||||
var isInactive = false;
|
var isInactive = false;
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,8 @@ class _DesktopWalletSelectionDropDownState extends State<DesktopWalletSelectionD
|
||||||
final litecoinIcon = Image.asset('assets/images/litecoin_icon.png', height: 24, width: 24);
|
final litecoinIcon = Image.asset('assets/images/litecoin_icon.png', height: 24, width: 24);
|
||||||
final havenIcon = Image.asset('assets/images/haven_logo.png', height: 24, width: 24);
|
final havenIcon = Image.asset('assets/images/haven_logo.png', height: 24, width: 24);
|
||||||
final ethereumIcon = Image.asset('assets/images/eth_icon.png', height: 24, width: 24);
|
final ethereumIcon = Image.asset('assets/images/eth_icon.png', height: 24, width: 24);
|
||||||
|
final nanoIcon = Image.asset('assets/images/nano_icon.png', height: 24, width: 24);
|
||||||
|
final bananoIcon = Image.asset('assets/images/nano_icon.png', height: 24, width: 24);
|
||||||
final nonWalletTypeIcon = Image.asset('assets/images/close.png', height: 24, width: 24);
|
final nonWalletTypeIcon = Image.asset('assets/images/close.png', height: 24, width: 24);
|
||||||
|
|
||||||
Image _newWalletImage(BuildContext context) => Image.asset(
|
Image _newWalletImage(BuildContext context) => Image.asset(
|
||||||
|
@ -141,6 +143,10 @@ class _DesktopWalletSelectionDropDownState extends State<DesktopWalletSelectionD
|
||||||
return havenIcon;
|
return havenIcon;
|
||||||
case WalletType.ethereum:
|
case WalletType.ethereum:
|
||||||
return ethereumIcon;
|
return ethereumIcon;
|
||||||
|
case WalletType.nano:
|
||||||
|
return nanoIcon;
|
||||||
|
case WalletType.banano:
|
||||||
|
return bananoIcon;
|
||||||
default:
|
default:
|
||||||
return nonWalletTypeIcon;
|
return nonWalletTypeIcon;
|
||||||
}
|
}
|
||||||
|
|
|
@ -120,31 +120,6 @@ class AddressPage extends BasePage {
|
||||||
Widget body(BuildContext context) {
|
Widget body(BuildContext context) {
|
||||||
_setEffects(context);
|
_setEffects(context);
|
||||||
|
|
||||||
autorun((_) async {
|
|
||||||
if (!dashboardViewModel.isOutdatedElectrumWallet ||
|
|
||||||
!dashboardViewModel.settingsStore.shouldShowReceiveWarning) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await Future<void>.delayed(Duration(seconds: 1));
|
|
||||||
if (context.mounted) {
|
|
||||||
await showPopUp<void>(
|
|
||||||
context: context,
|
|
||||||
builder: (BuildContext context) {
|
|
||||||
return AlertWithTwoActions(
|
|
||||||
alertTitle: S.of(context).pre_seed_title,
|
|
||||||
alertContent: S.of(context).outdated_electrum_wallet_receive_warning,
|
|
||||||
leftButtonText: S.of(context).understand,
|
|
||||||
actionLeftButton: () => Navigator.of(context).pop(),
|
|
||||||
rightButtonText: S.of(context).do_not_show_me,
|
|
||||||
actionRightButton: () {
|
|
||||||
dashboardViewModel.settingsStore.setShouldShowReceiveWarning(false);
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return KeyboardActions(
|
return KeyboardActions(
|
||||||
autoScroll: false,
|
autoScroll: false,
|
||||||
disableScroll: true,
|
disableScroll: true,
|
||||||
|
|
|
@ -18,18 +18,21 @@ class MenuWidget extends StatefulWidget {
|
||||||
|
|
||||||
class MenuWidgetState extends State<MenuWidget> {
|
class MenuWidgetState extends State<MenuWidget> {
|
||||||
MenuWidgetState()
|
MenuWidgetState()
|
||||||
: this.menuWidth = 0,
|
: this.menuWidth = 0,
|
||||||
this.screenWidth = 0,
|
this.screenWidth = 0,
|
||||||
this.screenHeight = 0,
|
this.screenHeight = 0,
|
||||||
this.headerHeight = 120,
|
this.headerHeight = 120,
|
||||||
this.tileHeight = 60,
|
this.tileHeight = 60,
|
||||||
this.fromTopEdge = 50,
|
this.fromTopEdge = 50,
|
||||||
this.fromBottomEdge = 25,
|
this.fromBottomEdge = 25,
|
||||||
this.moneroIcon = Image.asset('assets/images/monero_menu.png'),
|
this.moneroIcon = Image.asset('assets/images/monero_menu.png'),
|
||||||
this.bitcoinIcon = Image.asset('assets/images/bitcoin_menu.png'),
|
this.bitcoinIcon = Image.asset('assets/images/bitcoin_menu.png'),
|
||||||
this.litecoinIcon = Image.asset('assets/images/litecoin_menu.png'),
|
this.litecoinIcon = Image.asset('assets/images/litecoin_menu.png'),
|
||||||
this.havenIcon = Image.asset('assets/images/haven_menu.png'),
|
this.havenIcon = Image.asset('assets/images/haven_menu.png'),
|
||||||
this.ethereumIcon = Image.asset('assets/images/eth_icon.png');
|
this.ethereumIcon = Image.asset('assets/images/eth_icon.png'),
|
||||||
|
this.nanoIcon = Image.asset('assets/images/nano_icon.png'),
|
||||||
|
this.bananoIcon = Image.asset('assets/images/nano_icon.png');
|
||||||
|
|
||||||
|
|
||||||
final largeScreen = 731;
|
final largeScreen = 731;
|
||||||
|
|
||||||
|
@ -47,6 +50,9 @@ class MenuWidgetState extends State<MenuWidget> {
|
||||||
Image litecoinIcon;
|
Image litecoinIcon;
|
||||||
Image havenIcon;
|
Image havenIcon;
|
||||||
Image ethereumIcon;
|
Image ethereumIcon;
|
||||||
|
Image nanoIcon;
|
||||||
|
Image bananoIcon;
|
||||||
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
|
@ -206,6 +212,10 @@ class MenuWidgetState extends State<MenuWidget> {
|
||||||
return havenIcon;
|
return havenIcon;
|
||||||
case WalletType.ethereum:
|
case WalletType.ethereum:
|
||||||
return ethereumIcon;
|
return ethereumIcon;
|
||||||
|
case WalletType.nano:
|
||||||
|
return nanoIcon;
|
||||||
|
case WalletType.banano:
|
||||||
|
return bananoIcon;
|
||||||
default:
|
default:
|
||||||
throw Exception('No icon for ${type.toString()}');
|
throw Exception('No icon for ${type.toString()}');
|
||||||
}
|
}
|
||||||
|
|
103
lib/src/screens/nano/nano_change_rep_page.dart
Normal file
103
lib/src/screens/nano/nano_change_rep_page.dart
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
import 'package:cake_wallet/core/address_validator.dart';
|
||||||
|
import 'package:cake_wallet/nano/nano.dart';
|
||||||
|
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
|
||||||
|
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
|
||||||
|
import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
|
||||||
|
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||||
|
import 'package:cw_core/crypto_currency.dart';
|
||||||
|
import 'package:cw_core/wallet_base.dart';
|
||||||
|
import 'package:cw_nano/nano_wallet.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||||
|
import 'package:cake_wallet/generated/i18n.dart';
|
||||||
|
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||||
|
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
|
||||||
|
|
||||||
|
class NanoChangeRepPage extends BasePage {
|
||||||
|
NanoChangeRepPage(WalletBase wallet)
|
||||||
|
: _wallet = wallet,
|
||||||
|
_addressController = TextEditingController() {
|
||||||
|
_addressController.text = (wallet as NanoWallet).representative;
|
||||||
|
}
|
||||||
|
|
||||||
|
final TextEditingController _addressController;
|
||||||
|
final WalletBase _wallet;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get title => S.current.change_rep;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget body(BuildContext context) {
|
||||||
|
return Container(
|
||||||
|
padding: EdgeInsets.only(left: 24, right: 24),
|
||||||
|
child: ScrollableWithBottomSection(
|
||||||
|
contentPadding: EdgeInsets.only(bottom: 24.0),
|
||||||
|
content: Container(
|
||||||
|
child: Column(
|
||||||
|
children: <Widget>[
|
||||||
|
Row(
|
||||||
|
children: <Widget>[
|
||||||
|
Expanded(
|
||||||
|
child: BaseTextFormField(
|
||||||
|
controller: _addressController,
|
||||||
|
hintText: S.of(context).node_address,
|
||||||
|
validator: AddressValidator(type: CryptoCurrency.nano),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
bottomSectionPadding: EdgeInsets.only(bottom: 24),
|
||||||
|
bottomSection: Observer(
|
||||||
|
builder: (_) => Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: <Widget>[
|
||||||
|
Flexible(
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.only(right: 8.0),
|
||||||
|
child: LoadingPrimaryButton(
|
||||||
|
onPressed: () async {
|
||||||
|
final confirmed = await showPopUp<bool>(
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return AlertWithTwoActions(
|
||||||
|
alertTitle: S.of(context).change_rep,
|
||||||
|
alertContent: S.of(context).change_rep_message,
|
||||||
|
rightButtonText: S.of(context).change,
|
||||||
|
leftButtonText: S.of(context).cancel,
|
||||||
|
actionRightButton: () => Navigator.pop(context, true),
|
||||||
|
actionLeftButton: () => Navigator.pop(context, false));
|
||||||
|
}) ??
|
||||||
|
false;
|
||||||
|
|
||||||
|
if (confirmed) {
|
||||||
|
try {
|
||||||
|
await nano!.changeRep(_wallet, _addressController.text);
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
} catch (e) {
|
||||||
|
await showPopUp<void>(
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return AlertWithOneAction(
|
||||||
|
alertTitle: S.of(context).error,
|
||||||
|
alertContent: e.toString(),
|
||||||
|
buttonText: S.of(context).ok,
|
||||||
|
buttonAction: () => Navigator.pop(context));
|
||||||
|
});
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
text: S.of(context).change,
|
||||||
|
color: Theme.of(context).primaryColor,
|
||||||
|
textColor: Colors.white,
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
],
|
||||||
|
)),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,66 @@
|
||||||
|
import 'package:cake_wallet/core/execution_state.dart';
|
||||||
|
import 'package:cake_wallet/view_model/nano_account_list/nano_account_edit_or_create_view_model.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||||
|
import 'package:cake_wallet/generated/i18n.dart';
|
||||||
|
import 'package:cake_wallet/core/monero_account_label_validator.dart';
|
||||||
|
import 'package:cake_wallet/view_model/monero_account_list/monero_account_edit_or_create_view_model.dart';
|
||||||
|
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||||
|
import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
|
||||||
|
|
||||||
|
class NanoAccountEditOrCreatePage extends BasePage {
|
||||||
|
NanoAccountEditOrCreatePage({required this.nanoAccountCreationViewModel})
|
||||||
|
: _formKey = GlobalKey<FormState>(),
|
||||||
|
_textController = TextEditingController() {
|
||||||
|
_textController.addListener(() => nanoAccountCreationViewModel.label = _textController.text);
|
||||||
|
_textController.text = nanoAccountCreationViewModel.label;
|
||||||
|
}
|
||||||
|
|
||||||
|
final NanoAccountEditOrCreateViewModel nanoAccountCreationViewModel;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get title => S.current.account;
|
||||||
|
|
||||||
|
final GlobalKey<FormState> _formKey;
|
||||||
|
final TextEditingController _textController;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget body(BuildContext context) => Form(
|
||||||
|
key: _formKey,
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.all(24.0),
|
||||||
|
child: Column(
|
||||||
|
children: <Widget>[
|
||||||
|
Expanded(
|
||||||
|
child: Center(
|
||||||
|
child: BaseTextFormField(
|
||||||
|
controller: _textController,
|
||||||
|
hintText: S.of(context).account,
|
||||||
|
validator: MoneroLabelValidator(),
|
||||||
|
))),
|
||||||
|
Observer(
|
||||||
|
builder: (_) => LoadingPrimaryButton(
|
||||||
|
onPressed: () async {
|
||||||
|
if (_formKey.currentState != null && !_formKey.currentState!.validate()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await nanoAccountCreationViewModel.save();
|
||||||
|
|
||||||
|
Navigator.of(context).pop(_textController.text);
|
||||||
|
},
|
||||||
|
text: nanoAccountCreationViewModel.isEdit
|
||||||
|
? S.of(context).rename
|
||||||
|
: S.of(context).add,
|
||||||
|
color: Theme.of(context).primaryColor,
|
||||||
|
textColor: Colors.white,
|
||||||
|
isLoading: nanoAccountCreationViewModel.state is IsExecutingState,
|
||||||
|
isDisabled: nanoAccountCreationViewModel.label?.isEmpty ?? true,
|
||||||
|
))
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
92
lib/src/screens/nano_accounts/nano_account_list_page.dart
Normal file
92
lib/src/screens/nano_accounts/nano_account_list_page.dart
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
import 'package:cake_wallet/src/widgets/picker_inner_wrapper_widget.dart';
|
||||||
|
import 'package:cake_wallet/src/widgets/section_divider.dart';
|
||||||
|
import 'package:cake_wallet/view_model/nano_account_list/nano_account_list_view_model.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||||
|
import 'package:cake_wallet/routes.dart';
|
||||||
|
import 'package:cake_wallet/generated/i18n.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/monero_accounts/widgets/account_tile.dart';
|
||||||
|
|
||||||
|
class NanoAccountListPage extends StatelessWidget {
|
||||||
|
NanoAccountListPage({required this.accountListViewModel});
|
||||||
|
|
||||||
|
final NanoAccountListViewModel accountListViewModel;
|
||||||
|
final ScrollController controller = ScrollController();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
double itemHeight = 80;
|
||||||
|
double buttonHeight = 62;
|
||||||
|
|
||||||
|
return Observer(builder: (_) {
|
||||||
|
final accounts = accountListViewModel.accounts;
|
||||||
|
return PickerInnerWrapperWidget(
|
||||||
|
title: S.of(context).choose_account,
|
||||||
|
itemsHeight: (itemHeight * accounts.length) + buttonHeight,
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: Scrollbar(
|
||||||
|
controller: controller,
|
||||||
|
child: ListView.separated(
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
controller: controller,
|
||||||
|
separatorBuilder: (context, index) => const VerticalSectionDivider(),
|
||||||
|
itemCount: accounts.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final account = accounts[index];
|
||||||
|
|
||||||
|
return AccountTile(
|
||||||
|
isCurrent: account.isSelected,
|
||||||
|
accountName: account.label,
|
||||||
|
accountBalance: account.balance ?? '0.00',
|
||||||
|
currency: accountListViewModel.currency.toString(),
|
||||||
|
onTap: () {
|
||||||
|
if (account.isSelected) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
accountListViewModel.select(account);
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
onEdit: () async => await Navigator.of(context)
|
||||||
|
.pushNamed(Routes.nanoAccountCreation, arguments: account));
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
GestureDetector(
|
||||||
|
onTap: () async => await Navigator.of(context).pushNamed(Routes.nanoAccountCreation),
|
||||||
|
child: Container(
|
||||||
|
height: buttonHeight,
|
||||||
|
color: Theme.of(context).cardColor,
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 24),
|
||||||
|
child: Center(
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: <Widget>[
|
||||||
|
Icon(
|
||||||
|
Icons.add,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.only(left: 5),
|
||||||
|
child: Text(
|
||||||
|
S.of(context).create_new_account,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 15,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
fontFamily: 'Lato',
|
||||||
|
color: Colors.white,
|
||||||
|
decoration: TextDecoration.none,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
96
lib/src/screens/nano_accounts/widgets/account_tile.dart
Normal file
96
lib/src/screens/nano_accounts/widgets/account_tile.dart
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
import 'package:cake_wallet/themes/extensions/account_list_theme.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
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});
|
||||||
|
|
||||||
|
final bool isCurrent;
|
||||||
|
final String accountName;
|
||||||
|
final String? accountBalance;
|
||||||
|
final String currency;
|
||||||
|
final Function() onTap;
|
||||||
|
final Function() onEdit;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final color = isCurrent
|
||||||
|
? Theme.of(context).extension<AccountListTheme>()!.currentAccountBackgroundColor
|
||||||
|
: Theme.of(context).extension<AccountListTheme>()!.tilesBackgroundColor;
|
||||||
|
final textColor = isCurrent
|
||||||
|
? Theme.of(context).extension<AccountListTheme>()!.currentAccountTextColor
|
||||||
|
: Theme.of(context).extension<AccountListTheme>()!.tilesTextColor;
|
||||||
|
|
||||||
|
final Widget cell = GestureDetector(
|
||||||
|
onTap: onTap,
|
||||||
|
child: Container(
|
||||||
|
height: 77,
|
||||||
|
width: double.infinity,
|
||||||
|
padding: EdgeInsets.only(left: 24, right: 24),
|
||||||
|
color: color,
|
||||||
|
child: Wrap(
|
||||||
|
direction: Axis.horizontal,
|
||||||
|
alignment: WrapAlignment.spaceBetween,
|
||||||
|
runAlignment: WrapAlignment.center,
|
||||||
|
crossAxisAlignment: WrapCrossAlignment.center,
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
child: Text(
|
||||||
|
accountName,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
fontFamily: 'Lato',
|
||||||
|
color: textColor,
|
||||||
|
decoration: TextDecoration.none,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (accountBalance != null)
|
||||||
|
Container(
|
||||||
|
child: Text(
|
||||||
|
'${accountBalance.toString()} $currency',
|
||||||
|
textAlign: TextAlign.end,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 15,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
fontFamily: 'Lato',
|
||||||
|
color: Theme.of(context).textTheme.headlineMedium!.color!,
|
||||||
|
decoration: TextDecoration.none,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
// return cell;
|
||||||
|
return Slidable(
|
||||||
|
key: Key(accountName),
|
||||||
|
child: cell,
|
||||||
|
endActionPane: _actionPane(context)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ActionPane _actionPane(BuildContext context) => ActionPane(
|
||||||
|
motion: const ScrollMotion(),
|
||||||
|
extentRatio: 0.3,
|
||||||
|
children: [
|
||||||
|
SlidableAction(
|
||||||
|
onPressed: (_) => onEdit.call(),
|
||||||
|
backgroundColor: Colors.blue,
|
||||||
|
foregroundColor: Colors.white,
|
||||||
|
icon: Icons.edit,
|
||||||
|
label: S.of(context).edit,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
196
lib/src/screens/nodes/pow_node_create_or_edit_page.dart
Normal file
196
lib/src/screens/nodes/pow_node_create_or_edit_page.dart
Normal file
|
@ -0,0 +1,196 @@
|
||||||
|
import 'package:cake_wallet/core/execution_state.dart';
|
||||||
|
import 'package:cake_wallet/palette.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/nodes/widgets/node_form.dart';
|
||||||
|
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
|
||||||
|
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
|
||||||
|
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||||
|
import 'package:cake_wallet/view_model/node_list/node_create_or_edit_view_model.dart';
|
||||||
|
import 'package:cw_core/node.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||||
|
import 'package:mobx/mobx.dart';
|
||||||
|
import 'package:cake_wallet/generated/i18n.dart';
|
||||||
|
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||||
|
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
|
||||||
|
|
||||||
|
class PowNodeCreateOrEditPage extends BasePage {
|
||||||
|
PowNodeCreateOrEditPage({required this.nodeCreateOrEditViewModel,this.editingNode, this.isSelected})
|
||||||
|
: _formKey = GlobalKey<FormState>(),
|
||||||
|
_addressController = TextEditingController(),
|
||||||
|
_portController = TextEditingController(),
|
||||||
|
_loginController = TextEditingController(),
|
||||||
|
_passwordController = TextEditingController() {
|
||||||
|
reaction((_) => nodeCreateOrEditViewModel.address, (String address) {
|
||||||
|
if (address != _addressController.text) {
|
||||||
|
_addressController.text = address;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
reaction((_) => nodeCreateOrEditViewModel.port, (String port) {
|
||||||
|
if (port != _portController.text) {
|
||||||
|
_portController.text = port;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (nodeCreateOrEditViewModel.hasAuthCredentials) {
|
||||||
|
reaction((_) => nodeCreateOrEditViewModel.login, (String login) {
|
||||||
|
if (login != _loginController.text) {
|
||||||
|
_loginController.text = login;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
reaction((_) => nodeCreateOrEditViewModel.password, (String password) {
|
||||||
|
if (password != _passwordController.text) {
|
||||||
|
_passwordController.text = password;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_addressController.addListener(
|
||||||
|
() => nodeCreateOrEditViewModel.address = _addressController.text);
|
||||||
|
_portController.addListener(
|
||||||
|
() => nodeCreateOrEditViewModel.port = _portController.text);
|
||||||
|
_loginController.addListener(
|
||||||
|
() => nodeCreateOrEditViewModel.login = _loginController.text);
|
||||||
|
_passwordController.addListener(
|
||||||
|
() => nodeCreateOrEditViewModel.password = _passwordController.text);
|
||||||
|
}
|
||||||
|
|
||||||
|
final GlobalKey<FormState> _formKey;
|
||||||
|
final TextEditingController _addressController;
|
||||||
|
final TextEditingController _portController;
|
||||||
|
final TextEditingController _loginController;
|
||||||
|
final TextEditingController _passwordController;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get title => editingNode != null ? S.current.edit_node : S.current.node_new;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget trailing(BuildContext context) => IconButton(
|
||||||
|
onPressed: () async {
|
||||||
|
await nodeCreateOrEditViewModel.scanQRCodeForNewNode();
|
||||||
|
},
|
||||||
|
splashColor: Colors.transparent,
|
||||||
|
highlightColor: Colors.transparent,
|
||||||
|
hoverColor: Colors.transparent,
|
||||||
|
icon: Image.asset(
|
||||||
|
'assets/images/qr_code_icon.png',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
final NodeCreateOrEditViewModel nodeCreateOrEditViewModel;
|
||||||
|
final Node? editingNode;
|
||||||
|
final bool? isSelected;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget body(BuildContext context) {
|
||||||
|
|
||||||
|
reaction((_) => nodeCreateOrEditViewModel.connectionState,
|
||||||
|
(ExecutionState state) {
|
||||||
|
if (state is ExecutedSuccessfullyState) {
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
showPopUp<void>(
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext context) =>
|
||||||
|
AlertWithOneAction(
|
||||||
|
alertTitle: S.of(context).new_node_testing,
|
||||||
|
alertContent: state.payload as bool
|
||||||
|
? S.of(context).node_connection_successful
|
||||||
|
: S.of(context).node_connection_failed,
|
||||||
|
buttonText: S.of(context).ok,
|
||||||
|
buttonAction: () => Navigator.of(context).pop()));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state is FailureState) {
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
showPopUp<void>(
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return AlertWithOneAction(
|
||||||
|
alertTitle: S.of(context).error,
|
||||||
|
alertContent: state.error,
|
||||||
|
buttonText: S.of(context).ok,
|
||||||
|
buttonAction: () => Navigator.of(context).pop());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return Container(
|
||||||
|
padding: EdgeInsets.only(left: 24, right: 24),
|
||||||
|
child: ScrollableWithBottomSection(
|
||||||
|
contentPadding: EdgeInsets.only(bottom: 24.0),
|
||||||
|
content: NodeForm(
|
||||||
|
formKey: _formKey,
|
||||||
|
nodeViewModel: nodeCreateOrEditViewModel,
|
||||||
|
editingNode: editingNode,
|
||||||
|
),
|
||||||
|
bottomSectionPadding: EdgeInsets.only(bottom: 24),
|
||||||
|
bottomSection: Observer(
|
||||||
|
builder: (_) => Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: <Widget>[
|
||||||
|
Flexible(
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.only(right: 8.0),
|
||||||
|
child: LoadingPrimaryButton(
|
||||||
|
onPressed: () async {
|
||||||
|
final confirmed = await showPopUp<bool>(
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return AlertWithTwoActions(
|
||||||
|
alertTitle:
|
||||||
|
S.of(context).remove_node,
|
||||||
|
alertContent: S
|
||||||
|
.of(context)
|
||||||
|
.remove_node_message,
|
||||||
|
rightButtonText:
|
||||||
|
S.of(context).remove,
|
||||||
|
leftButtonText:
|
||||||
|
S.of(context).cancel,
|
||||||
|
actionRightButton: () =>
|
||||||
|
Navigator.pop(context, true),
|
||||||
|
actionLeftButton: () =>
|
||||||
|
Navigator.pop(context, false));
|
||||||
|
}) ??
|
||||||
|
false;
|
||||||
|
|
||||||
|
if (confirmed) {
|
||||||
|
await editingNode!.delete();
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
text: S.of(context).delete,
|
||||||
|
isDisabled: !nodeCreateOrEditViewModel.isReady ||
|
||||||
|
(isSelected ?? false),
|
||||||
|
color: Palette.red,
|
||||||
|
textColor: Colors.white),
|
||||||
|
)),
|
||||||
|
Flexible(
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.only(left: 8.0),
|
||||||
|
child: PrimaryButton(
|
||||||
|
onPressed: () async {
|
||||||
|
if (_formKey.currentState != null && !_formKey.currentState!.validate()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await nodeCreateOrEditViewModel.save(
|
||||||
|
editingNode: editingNode, saveAsCurrent: isSelected ?? false);
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
text: S.of(context).save,
|
||||||
|
color: Theme.of(context).primaryColor,
|
||||||
|
textColor: Colors.white,
|
||||||
|
isDisabled: (!nodeCreateOrEditViewModel.isReady)||
|
||||||
|
(nodeCreateOrEditViewModel
|
||||||
|
.connectionState is IsExecutingState),
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
],
|
||||||
|
)),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,10 +11,12 @@ class NodeListRow extends StandardListRow {
|
||||||
{required String title,
|
{required String title,
|
||||||
required this.node,
|
required this.node,
|
||||||
required void Function(BuildContext context) onTap,
|
required void Function(BuildContext context) onTap,
|
||||||
required bool isSelected})
|
required bool isSelected,
|
||||||
|
required this.isPow})
|
||||||
: super(title: title, onTap: onTap, isSelected: isSelected);
|
: super(title: title, onTap: onTap, isSelected: isSelected);
|
||||||
|
|
||||||
final Node node;
|
final Node node;
|
||||||
|
final bool isPow;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget buildLeading(BuildContext context) {
|
Widget buildLeading(BuildContext context) {
|
||||||
|
@ -33,7 +35,7 @@ class NodeListRow extends StandardListRow {
|
||||||
@override
|
@override
|
||||||
Widget buildTrailing(BuildContext context) {
|
Widget buildTrailing(BuildContext context) {
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: () => Navigator.of(context).pushNamed(Routes.newNode,
|
onTap: () => Navigator.of(context).pushNamed(isPow ? Routes.newPowNode : Routes.newNode,
|
||||||
arguments: {'editingNode': node, 'isSelected': isSelected}),
|
arguments: {'editingNode': node, 'isSelected': isSelected}),
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: EdgeInsets.all(10),
|
padding: EdgeInsets.all(10),
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
|
import 'package:cake_wallet/src/screens/nano_accounts/nano_account_list_page.dart';
|
||||||
|
import 'package:cake_wallet/src/widgets/keyboard_done_button.dart';
|
||||||
|
import 'package:cake_wallet/themes/extensions/balance_page_theme.dart';
|
||||||
import 'package:cake_wallet/themes/extensions/keyboard_theme.dart';
|
import 'package:cake_wallet/themes/extensions/keyboard_theme.dart';
|
||||||
import 'package:cake_wallet/themes/extensions/receive_page_theme.dart';
|
import 'package:cake_wallet/themes/extensions/receive_page_theme.dart';
|
||||||
import 'package:cake_wallet/src/widgets/gradient_background.dart';
|
import 'package:cake_wallet/src/widgets/gradient_background.dart';
|
||||||
import 'package:cake_wallet/src/widgets/keyboard_done_button.dart';
|
|
||||||
import 'package:cake_wallet/src/widgets/section_divider.dart';
|
import 'package:cake_wallet/src/widgets/section_divider.dart';
|
||||||
import 'package:cake_wallet/themes/theme_base.dart';
|
import 'package:cake_wallet/themes/theme_base.dart';
|
||||||
import 'package:cake_wallet/utils/share_util.dart';
|
import 'package:cake_wallet/utils/share_util.dart';
|
||||||
|
@ -22,7 +24,6 @@ import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_i
|
||||||
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_view_model.dart';
|
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_view_model.dart';
|
||||||
import 'package:cake_wallet/src/screens/receive/widgets/qr_widget.dart';
|
import 'package:cake_wallet/src/screens/receive/widgets/qr_widget.dart';
|
||||||
import 'package:keyboard_actions/keyboard_actions.dart';
|
import 'package:keyboard_actions/keyboard_actions.dart';
|
||||||
import 'package:cake_wallet/themes/extensions/balance_page_theme.dart';
|
|
||||||
|
|
||||||
class ReceivePage extends BasePage {
|
class ReceivePage extends BasePage {
|
||||||
ReceivePage({required this.addressListViewModel})
|
ReceivePage({required this.addressListViewModel})
|
||||||
|
@ -99,7 +100,9 @@ class ReceivePage extends BasePage {
|
||||||
@override
|
@override
|
||||||
Widget body(BuildContext context) {
|
Widget body(BuildContext context) {
|
||||||
return (addressListViewModel.type == WalletType.monero ||
|
return (addressListViewModel.type == WalletType.monero ||
|
||||||
addressListViewModel.type == WalletType.haven)
|
addressListViewModel.type == WalletType.haven ||
|
||||||
|
addressListViewModel.type == WalletType.nano ||
|
||||||
|
addressListViewModel.type == WalletType.banano)
|
||||||
? KeyboardActions(
|
? KeyboardActions(
|
||||||
config: KeyboardActionsConfig(
|
config: KeyboardActionsConfig(
|
||||||
keyboardActionsPlatform: KeyboardActionsPlatform.IOS,
|
keyboardActionsPlatform: KeyboardActionsPlatform.IOS,
|
||||||
|
@ -137,9 +140,18 @@ class ReceivePage extends BasePage {
|
||||||
|
|
||||||
if (item is WalletAccountListHeader) {
|
if (item is WalletAccountListHeader) {
|
||||||
cell = HeaderTile(
|
cell = HeaderTile(
|
||||||
onTap: () async => await showPopUp<void>(
|
onTap: () async {
|
||||||
context: context,
|
if (addressListViewModel.type == WalletType.monero ||
|
||||||
builder: (_) => getIt.get<MoneroAccountListPage>()),
|
addressListViewModel.type == WalletType.haven) {
|
||||||
|
await showPopUp<void>(
|
||||||
|
context: context,
|
||||||
|
builder: (_) => getIt.get<MoneroAccountListPage>());
|
||||||
|
} else {
|
||||||
|
await showPopUp<void>(
|
||||||
|
context: context,
|
||||||
|
builder: (_) => getIt.get<NanoAccountListPage>());
|
||||||
|
}
|
||||||
|
},
|
||||||
title: S.of(context).accounts,
|
title: S.of(context).accounts,
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
Icons.arrow_forward_ios,
|
Icons.arrow_forward_ios,
|
||||||
|
|
129
lib/src/screens/restore/wallet_restore_choose_derivation.dart
Normal file
129
lib/src/screens/restore/wallet_restore_choose_derivation.dart
Normal file
|
@ -0,0 +1,129 @@
|
||||||
|
import 'package:cake_wallet/di.dart';
|
||||||
|
import 'package:cake_wallet/generated/i18n.dart';
|
||||||
|
import 'package:cake_wallet/store/settings_store.dart';
|
||||||
|
import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
|
||||||
|
import 'package:cake_wallet/themes/theme_base.dart';
|
||||||
|
import 'package:cake_wallet/view_model/wallet_restore_choose_derivation_view_model.dart';
|
||||||
|
import 'package:cw_core/wallet_info.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||||
|
|
||||||
|
class WalletRestoreChooseDerivationPage extends BasePage {
|
||||||
|
WalletRestoreChooseDerivationPage(this.walletRestoreChooseDerivationViewModel) {}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget middle(BuildContext context) => Observer(
|
||||||
|
builder: (_) => Text(
|
||||||
|
S.current.choose_derivation,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 18.0,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
fontFamily: 'Lato',
|
||||||
|
color: titleColor(context)),
|
||||||
|
));
|
||||||
|
|
||||||
|
final WalletRestoreChooseDerivationViewModel walletRestoreChooseDerivationViewModel;
|
||||||
|
DerivationType derivationType = DerivationType.unknown;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget body(BuildContext context) {
|
||||||
|
return Observer(
|
||||||
|
builder: (_) => FutureBuilder<List<DerivationInfo>>(
|
||||||
|
future: walletRestoreChooseDerivationViewModel.derivations,
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
if (snapshot.connectionState == ConnectionState.waiting) {
|
||||||
|
return Center(
|
||||||
|
child: CircularProgressIndicator(),
|
||||||
|
);
|
||||||
|
} else if (snapshot.hasError) {
|
||||||
|
return Center(child: Text('Error: ${snapshot.error}'));
|
||||||
|
} else if (!snapshot.hasData || snapshot.data!.isEmpty) {
|
||||||
|
return Center(child: Text('Error! No derivations available!'));
|
||||||
|
} else {
|
||||||
|
return ListView.separated(
|
||||||
|
shrinkWrap: true,
|
||||||
|
separatorBuilder: (_, __) => SizedBox(),
|
||||||
|
itemCount: snapshot.data!.length,
|
||||||
|
itemBuilder: (__, index) {
|
||||||
|
final derivation = snapshot.data![index];
|
||||||
|
return Card(
|
||||||
|
margin: const EdgeInsets.all(8),
|
||||||
|
elevation: 3,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(15),
|
||||||
|
),
|
||||||
|
child: InkWell(
|
||||||
|
borderRadius: BorderRadius.circular(15),
|
||||||
|
onTap: () async {
|
||||||
|
Navigator.pop(context, derivation);
|
||||||
|
},
|
||||||
|
child: ListTile(
|
||||||
|
contentPadding: EdgeInsets.all(16),
|
||||||
|
title: Center(
|
||||||
|
child: Text(
|
||||||
|
"${derivation.description ?? derivation.derivationType.toString().split('.').last}",
|
||||||
|
style: Theme.of(context).primaryTextTheme.labelMedium!.copyWith(
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: FontWeight.w800,
|
||||||
|
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
subtitle: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
if (derivation.derivationPath != null)
|
||||||
|
Text(
|
||||||
|
derivation.derivationPath!,
|
||||||
|
style: Theme.of(context).primaryTextTheme.labelMedium!.copyWith(
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
color: Theme.of(context)
|
||||||
|
.extension<CakeTextTheme>()!
|
||||||
|
.secondaryTextColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
derivation.address,
|
||||||
|
style: Theme.of(context).primaryTextTheme.labelMedium!.copyWith(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
color: Theme.of(context)
|
||||||
|
.extension<CakeTextTheme>()!
|
||||||
|
.secondaryTextColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
"${S.current.confirmed}: ${derivation.balance}",
|
||||||
|
style: Theme.of(context).primaryTextTheme.labelMedium!.copyWith(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
color: Theme.of(context)
|
||||||
|
.extension<CakeTextTheme>()!
|
||||||
|
.secondaryTextColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
"${S.current.transactions}: ${derivation.height}",
|
||||||
|
style: Theme.of(context).primaryTextTheme.labelMedium!.copyWith(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
color: Theme.of(context)
|
||||||
|
.extension<CakeTextTheme>()!
|
||||||
|
.secondaryTextColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,6 +13,7 @@ import 'package:flutter/services.dart';
|
||||||
class WalletRestoreFromKeysFrom extends StatefulWidget {
|
class WalletRestoreFromKeysFrom extends StatefulWidget {
|
||||||
WalletRestoreFromKeysFrom({
|
WalletRestoreFromKeysFrom({
|
||||||
required this.walletRestoreViewModel,
|
required this.walletRestoreViewModel,
|
||||||
|
required this.onPrivateKeyChange,
|
||||||
required this.displayPrivateKeyField,
|
required this.displayPrivateKeyField,
|
||||||
required this.onHeightOrDateEntered,
|
required this.onHeightOrDateEntered,
|
||||||
Key? key,
|
Key? key,
|
||||||
|
@ -20,6 +21,7 @@ class WalletRestoreFromKeysFrom extends StatefulWidget {
|
||||||
|
|
||||||
final Function(bool) onHeightOrDateEntered;
|
final Function(bool) onHeightOrDateEntered;
|
||||||
final WalletRestoreViewModel walletRestoreViewModel;
|
final WalletRestoreViewModel walletRestoreViewModel;
|
||||||
|
final void Function(String)? onPrivateKeyChange;
|
||||||
final bool displayPrivateKeyField;
|
final bool displayPrivateKeyField;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -54,6 +56,7 @@ class WalletRestoreFromKeysFromState extends State<WalletRestoreFromKeysFrom> {
|
||||||
if (privateKeyController.text.isNotEmpty) {
|
if (privateKeyController.text.isNotEmpty) {
|
||||||
widget.onHeightOrDateEntered(true);
|
widget.onHeightOrDateEntered(true);
|
||||||
}
|
}
|
||||||
|
widget.onPrivateKeyChange?.call(privateKeyController.text);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,8 +65,8 @@ class WalletRestoreFromKeysFromState extends State<WalletRestoreFromKeysFrom> {
|
||||||
nameController.dispose();
|
nameController.dispose();
|
||||||
addressController.dispose();
|
addressController.dispose();
|
||||||
viewKeyController.dispose();
|
viewKeyController.dispose();
|
||||||
spendKeyController.dispose();
|
|
||||||
privateKeyController.dispose();
|
privateKeyController.dispose();
|
||||||
|
spendKeyController.dispose();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,12 @@
|
||||||
|
import 'package:cake_wallet/di.dart';
|
||||||
|
import 'package:cake_wallet/nano/nano.dart';
|
||||||
|
import 'package:cake_wallet/routes.dart';
|
||||||
|
import 'package:cake_wallet/store/app_store.dart';
|
||||||
import 'package:cake_wallet/themes/extensions/keyboard_theme.dart';
|
import 'package:cake_wallet/themes/extensions/keyboard_theme.dart';
|
||||||
import 'package:cake_wallet/src/widgets/keyboard_done_button.dart';
|
import 'package:cake_wallet/src/widgets/keyboard_done_button.dart';
|
||||||
import 'package:cake_wallet/utils/responsive_layout_util.dart';
|
import 'package:cake_wallet/utils/responsive_layout_util.dart';
|
||||||
|
import 'package:cw_core/nano_account_info_response.dart';
|
||||||
|
import 'package:cw_core/wallet_info.dart';
|
||||||
import 'package:cw_core/wallet_type.dart';
|
import 'package:cw_core/wallet_type.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:keyboard_actions/keyboard_actions.dart';
|
import 'package:keyboard_actions/keyboard_actions.dart';
|
||||||
|
@ -69,6 +75,12 @@ class WalletRestorePage extends BasePage {
|
||||||
_pages.add(WalletRestoreFromKeysFrom(
|
_pages.add(WalletRestoreFromKeysFrom(
|
||||||
key: walletRestoreFromKeysFormKey,
|
key: walletRestoreFromKeysFormKey,
|
||||||
walletRestoreViewModel: walletRestoreViewModel,
|
walletRestoreViewModel: walletRestoreViewModel,
|
||||||
|
onPrivateKeyChange: (String seed) {
|
||||||
|
if (walletRestoreViewModel.type == WalletType.nano ||
|
||||||
|
walletRestoreViewModel.type == WalletType.banano) {
|
||||||
|
walletRestoreViewModel.isButtonEnabled = _isValidSeedKey();
|
||||||
|
}
|
||||||
|
},
|
||||||
displayPrivateKeyField: walletRestoreViewModel.hasRestoreFromPrivateKey,
|
displayPrivateKeyField: walletRestoreViewModel.hasRestoreFromPrivateKey,
|
||||||
onHeightOrDateEntered: (value) => walletRestoreViewModel.isButtonEnabled = value));
|
onHeightOrDateEntered: (value) => walletRestoreViewModel.isButtonEnabled = value));
|
||||||
break;
|
break;
|
||||||
|
@ -97,6 +109,8 @@ class WalletRestorePage extends BasePage {
|
||||||
final GlobalKey<WalletRestoreFromSeedFormState> walletRestoreFromSeedFormKey;
|
final GlobalKey<WalletRestoreFromSeedFormState> walletRestoreFromSeedFormKey;
|
||||||
final GlobalKey<WalletRestoreFromKeysFromState> walletRestoreFromKeysFormKey;
|
final GlobalKey<WalletRestoreFromKeysFromState> walletRestoreFromKeysFormKey;
|
||||||
final FocusNode _blockHeightFocusNode;
|
final FocusNode _blockHeightFocusNode;
|
||||||
|
DerivationType derivationType = DerivationType.unknown;
|
||||||
|
String? derivationPath = null;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget body(BuildContext context) {
|
Widget body(BuildContext context) {
|
||||||
|
@ -185,7 +199,9 @@ class WalletRestorePage extends BasePage {
|
||||||
child: Observer(
|
child: Observer(
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
return LoadingPrimaryButton(
|
return LoadingPrimaryButton(
|
||||||
onPressed: _confirmForm,
|
onPressed: () async {
|
||||||
|
await _confirmForm(context);
|
||||||
|
},
|
||||||
text: S.of(context).restore_recover,
|
text: S.of(context).restore_recover,
|
||||||
color: Theme.of(context)
|
color: Theme.of(context)
|
||||||
.extension<WalletListTheme>()!
|
.extension<WalletListTheme>()!
|
||||||
|
@ -217,18 +233,34 @@ class WalletRestorePage extends BasePage {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((walletRestoreViewModel.type == WalletType.bitcoin ||
|
if ((walletRestoreViewModel.type == WalletType.litecoin) &&
|
||||||
walletRestoreViewModel.type == WalletType.litecoin) &&
|
|
||||||
(seedWords.length != WalletRestoreViewModelBase.electrumSeedMnemonicLength &&
|
(seedWords.length != WalletRestoreViewModelBase.electrumSeedMnemonicLength &&
|
||||||
seedWords.length != WalletRestoreViewModelBase.electrumShortSeedMnemonicLength)) {
|
seedWords.length != WalletRestoreViewModelBase.electrumShortSeedMnemonicLength)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// bip39:
|
||||||
|
const validSeedLengths = [12, 18, 24];
|
||||||
|
if (walletRestoreViewModel.type == WalletType.bitcoin &&
|
||||||
|
!(validSeedLengths.contains(seedWords.length))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
final words =
|
final words =
|
||||||
walletRestoreFromSeedFormKey.currentState!.seedWidgetStateKey.currentState!.words.toSet();
|
walletRestoreFromSeedFormKey.currentState!.seedWidgetStateKey.currentState!.words.toSet();
|
||||||
return seedWords.toSet().difference(words).toSet().isEmpty;
|
return seedWords.toSet().difference(words).toSet().isEmpty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool _isValidSeedKey() {
|
||||||
|
final seedKey = walletRestoreFromKeysFormKey.currentState!.privateKeyController.text;
|
||||||
|
|
||||||
|
if (seedKey.length != 64 && seedKey.length != 128) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
Map<String, dynamic> _credentials() {
|
Map<String, dynamic> _credentials() {
|
||||||
final credentials = <String, dynamic>{};
|
final credentials = <String, dynamic>{};
|
||||||
|
|
||||||
|
@ -243,10 +275,12 @@ class WalletRestorePage extends BasePage {
|
||||||
|
|
||||||
credentials['name'] =
|
credentials['name'] =
|
||||||
walletRestoreFromSeedFormKey.currentState!.nameTextEditingController.text;
|
walletRestoreFromSeedFormKey.currentState!.nameTextEditingController.text;
|
||||||
} else {
|
} else if (walletRestoreViewModel.mode == WalletRestoreMode.keys) {
|
||||||
if (walletRestoreViewModel.hasRestoreFromPrivateKey) {
|
if (walletRestoreViewModel.hasRestoreFromPrivateKey) {
|
||||||
credentials['private_key'] =
|
credentials['private_key'] =
|
||||||
walletRestoreFromKeysFormKey.currentState!.privateKeyController.text;
|
walletRestoreFromKeysFormKey.currentState!.privateKeyController.text;
|
||||||
|
credentials['name'] =
|
||||||
|
walletRestoreFromKeysFormKey.currentState!.nameTextEditingController.text;
|
||||||
} else {
|
} else {
|
||||||
credentials['address'] = walletRestoreFromKeysFormKey.currentState!.addressController.text;
|
credentials['address'] = walletRestoreFromKeysFormKey.currentState!.addressController.text;
|
||||||
credentials['viewKey'] = walletRestoreFromKeysFormKey.currentState!.viewKeyController.text;
|
credentials['viewKey'] = walletRestoreFromKeysFormKey.currentState!.viewKeyController.text;
|
||||||
|
@ -254,31 +288,81 @@ class WalletRestorePage extends BasePage {
|
||||||
walletRestoreFromKeysFormKey.currentState!.spendKeyController.text;
|
walletRestoreFromKeysFormKey.currentState!.spendKeyController.text;
|
||||||
credentials['height'] =
|
credentials['height'] =
|
||||||
walletRestoreFromKeysFormKey.currentState!.blockchainHeightKey.currentState!.height;
|
walletRestoreFromKeysFormKey.currentState!.blockchainHeightKey.currentState!.height;
|
||||||
|
credentials['name'] =
|
||||||
|
walletRestoreFromKeysFormKey.currentState!.nameTextEditingController.text;
|
||||||
}
|
}
|
||||||
|
|
||||||
credentials['name'] =
|
|
||||||
walletRestoreFromKeysFormKey.currentState!.nameTextEditingController.text;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
credentials['derivationType'] = this.derivationType;
|
||||||
|
credentials['derivationPath'] = this.derivationPath;
|
||||||
|
credentials['walletType'] = walletRestoreViewModel.type;
|
||||||
return credentials;
|
return credentials;
|
||||||
}
|
}
|
||||||
|
|
||||||
void _confirmForm() {
|
Future<List<DerivationInfo>> getDerivationInfo(dynamic credentials) async {
|
||||||
|
var list = <DerivationInfo>[];
|
||||||
|
var walletType = credentials["walletType"] as WalletType;
|
||||||
|
var appStore = getIt.get<AppStore>();
|
||||||
|
var node = appStore.settingsStore.getCurrentNode(walletType);
|
||||||
|
|
||||||
|
switch (walletType) {
|
||||||
|
case WalletType.nano:
|
||||||
|
String? mnemonic = credentials['seed'] as String?;
|
||||||
|
String? seedKey = credentials['private_key'] as String?;
|
||||||
|
AccountInfoResponse? bip39Info = await nanoUtil!.getInfoFromSeedOrMnemonic(
|
||||||
|
DerivationType.bip39,
|
||||||
|
mnemonic: mnemonic,
|
||||||
|
seedKey: seedKey,
|
||||||
|
node: node);
|
||||||
|
AccountInfoResponse? standardInfo = await nanoUtil!.getInfoFromSeedOrMnemonic(
|
||||||
|
DerivationType.nano,
|
||||||
|
mnemonic: mnemonic,
|
||||||
|
seedKey: seedKey,
|
||||||
|
node: node,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (standardInfo?.balance != null) {
|
||||||
|
list.add(DerivationInfo(
|
||||||
|
derivationType: DerivationType.nano,
|
||||||
|
balance: nanoUtil!.getRawAsUsableString(standardInfo!.balance, nanoUtil!.rawPerNano),
|
||||||
|
address: standardInfo.address!,
|
||||||
|
height: standardInfo.confirmationHeight,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bip39Info?.balance != null) {
|
||||||
|
list.add(DerivationInfo(
|
||||||
|
derivationType: DerivationType.bip39,
|
||||||
|
balance: nanoUtil!.getRawAsUsableString(bip39Info!.balance, nanoUtil!.rawPerNano),
|
||||||
|
address: bip39Info.address!,
|
||||||
|
height: bip39Info.confirmationHeight,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _confirmForm(BuildContext context) async {
|
||||||
// Dismissing all visible keyboard to provide context for navigation
|
// Dismissing all visible keyboard to provide context for navigation
|
||||||
FocusManager.instance.primaryFocus?.unfocus();
|
FocusManager.instance.primaryFocus?.unfocus();
|
||||||
final formContext = walletRestoreViewModel.mode == WalletRestoreMode.seed
|
|
||||||
? walletRestoreFromSeedFormKey.currentContext
|
|
||||||
: walletRestoreFromKeysFormKey.currentContext;
|
|
||||||
|
|
||||||
final formKey = walletRestoreViewModel.mode == WalletRestoreMode.seed
|
late BuildContext? formContext;
|
||||||
? walletRestoreFromSeedFormKey.currentState!.formKey
|
late GlobalKey<FormState>? formKey;
|
||||||
: walletRestoreFromKeysFormKey.currentState!.formKey;
|
late String name;
|
||||||
|
if (walletRestoreViewModel.mode == WalletRestoreMode.seed) {
|
||||||
|
formContext = walletRestoreFromSeedFormKey.currentContext;
|
||||||
|
formKey = walletRestoreFromSeedFormKey.currentState!.formKey;
|
||||||
|
name = walletRestoreFromSeedFormKey.currentState!.nameTextEditingController.value.text;
|
||||||
|
} else if (walletRestoreViewModel.mode == WalletRestoreMode.keys) {
|
||||||
|
formContext = walletRestoreFromKeysFormKey.currentContext;
|
||||||
|
formKey = walletRestoreFromKeysFormKey.currentState!.formKey;
|
||||||
|
name = walletRestoreFromKeysFormKey.currentState!.nameTextEditingController.value.text;
|
||||||
|
}
|
||||||
|
|
||||||
final name = walletRestoreViewModel.mode == WalletRestoreMode.seed
|
if (!formKey!.currentState!.validate()) {
|
||||||
? walletRestoreFromSeedFormKey.currentState!.nameTextEditingController.value.text
|
|
||||||
: walletRestoreFromKeysFormKey.currentState!.nameTextEditingController.value.text;
|
|
||||||
|
|
||||||
if (!formKey.currentState!.validate()) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -287,6 +371,54 @@ class WalletRestorePage extends BasePage {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
walletRestoreViewModel.state = IsExecutingState();
|
||||||
|
|
||||||
|
List<DerivationType> derivationTypes =
|
||||||
|
await walletRestoreViewModel.getDerivationTypes(_credentials());
|
||||||
|
|
||||||
|
if (derivationTypes[0] == DerivationType.unknown || derivationTypes.length > 1) {
|
||||||
|
// push screen to choose the derivation type:
|
||||||
|
List<DerivationInfo> derivations = await getDerivationInfo(_credentials());
|
||||||
|
|
||||||
|
int derivationsWithHistory = 0;
|
||||||
|
int derivationWithHistoryIndex = 0;
|
||||||
|
for (int i = 0; i < derivations.length; i++) {
|
||||||
|
if (derivations[i].height > 0) {
|
||||||
|
derivationsWithHistory++;
|
||||||
|
derivationWithHistoryIndex = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DerivationInfo? derivationInfo;
|
||||||
|
|
||||||
|
if (derivationsWithHistory > 1) {
|
||||||
|
derivationInfo = await Navigator.of(context).pushNamed(Routes.restoreWalletChooseDerivation,
|
||||||
|
arguments: derivations) as DerivationInfo?;
|
||||||
|
} else if (derivationsWithHistory == 1) {
|
||||||
|
derivationInfo = derivations[derivationWithHistoryIndex];
|
||||||
|
} else if (derivationsWithHistory == 0) {
|
||||||
|
// default derivation:
|
||||||
|
derivationInfo = DerivationInfo(
|
||||||
|
derivationType: derivationTypes[0],
|
||||||
|
derivationPath: "m/0'/1",
|
||||||
|
height: 0,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (derivationInfo == null) {
|
||||||
|
walletRestoreViewModel.state = InitialExecutionState();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.derivationType = derivationInfo.derivationType;
|
||||||
|
this.derivationPath = derivationInfo.derivationPath;
|
||||||
|
} else {
|
||||||
|
// electrum derivation:
|
||||||
|
this.derivationType = derivationTypes[0];
|
||||||
|
this.derivationPath = "m/0'/1";
|
||||||
|
}
|
||||||
|
|
||||||
|
walletRestoreViewModel.state = InitialExecutionState();
|
||||||
|
|
||||||
walletRestoreViewModel.create(options: _credentials());
|
walletRestoreViewModel.create(options: _credentials());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,26 +8,26 @@ import 'package:cake_wallet/src/widgets/cake_scrollbar.dart';
|
||||||
import 'package:flutter/scheduler.dart';
|
import 'package:flutter/scheduler.dart';
|
||||||
|
|
||||||
class ConfirmSendingAlert extends BaseAlertDialog {
|
class ConfirmSendingAlert extends BaseAlertDialog {
|
||||||
ConfirmSendingAlert({
|
ConfirmSendingAlert(
|
||||||
required this.alertTitle,
|
{required this.alertTitle,
|
||||||
this.paymentId,
|
this.paymentId,
|
||||||
this.paymentIdValue,
|
this.paymentIdValue,
|
||||||
required this.amount,
|
required this.amount,
|
||||||
required this.amountValue,
|
required this.amountValue,
|
||||||
required this.fiatAmountValue,
|
required this.fiatAmountValue,
|
||||||
required this.fee,
|
required this.fee,
|
||||||
required this.feeValue,
|
required this.feeValue,
|
||||||
required this.feeFiatAmount,
|
required this.feeFiatAmount,
|
||||||
required this.outputs,
|
required this.outputs,
|
||||||
required this.leftButtonText,
|
required this.leftButtonText,
|
||||||
required this.rightButtonText,
|
required this.rightButtonText,
|
||||||
required this.actionLeftButton,
|
required this.actionLeftButton,
|
||||||
required this.actionRightButton,
|
required this.actionRightButton,
|
||||||
this.alertBarrierDismissible = true,
|
this.alertBarrierDismissible = true,
|
||||||
this.alertLeftActionButtonTextColor,
|
this.alertLeftActionButtonTextColor,
|
||||||
this.alertRightActionButtonTextColor,
|
this.alertRightActionButtonTextColor,
|
||||||
this.alertLeftActionButtonColor,
|
this.alertLeftActionButtonColor,
|
||||||
this.alertRightActionButtonColor});
|
this.alertRightActionButtonColor});
|
||||||
|
|
||||||
final String alertTitle;
|
final String alertTitle;
|
||||||
final String? paymentId;
|
final String? paymentId;
|
||||||
|
@ -92,21 +92,20 @@ class ConfirmSendingAlert extends BaseAlertDialog {
|
||||||
fee: fee,
|
fee: fee,
|
||||||
feeValue: feeValue,
|
feeValue: feeValue,
|
||||||
feeFiatAmount: feeFiatAmount,
|
feeFiatAmount: feeFiatAmount,
|
||||||
outputs: outputs
|
outputs: outputs);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class ConfirmSendingAlertContent extends StatefulWidget {
|
class ConfirmSendingAlertContent extends StatefulWidget {
|
||||||
ConfirmSendingAlertContent({
|
ConfirmSendingAlertContent(
|
||||||
this.paymentId,
|
{this.paymentId,
|
||||||
this.paymentIdValue,
|
this.paymentIdValue,
|
||||||
required this.amount,
|
required this.amount,
|
||||||
required this.amountValue,
|
required this.amountValue,
|
||||||
required this.fiatAmountValue,
|
required this.fiatAmountValue,
|
||||||
required this.fee,
|
required this.fee,
|
||||||
required this.feeValue,
|
required this.feeValue,
|
||||||
required this.feeFiatAmount,
|
required this.feeFiatAmount,
|
||||||
required this.outputs});
|
required this.outputs});
|
||||||
|
|
||||||
final String? paymentId;
|
final String? paymentId;
|
||||||
final String? paymentIdValue;
|
final String? paymentIdValue;
|
||||||
|
@ -120,29 +119,28 @@ class ConfirmSendingAlertContent extends StatefulWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
ConfirmSendingAlertContentState createState() => ConfirmSendingAlertContentState(
|
ConfirmSendingAlertContentState createState() => ConfirmSendingAlertContentState(
|
||||||
paymentId: paymentId,
|
paymentId: paymentId,
|
||||||
paymentIdValue: paymentIdValue,
|
paymentIdValue: paymentIdValue,
|
||||||
amount: amount,
|
amount: amount,
|
||||||
amountValue: amountValue,
|
amountValue: amountValue,
|
||||||
fiatAmountValue: fiatAmountValue,
|
fiatAmountValue: fiatAmountValue,
|
||||||
fee: fee,
|
fee: fee,
|
||||||
feeValue: feeValue,
|
feeValue: feeValue,
|
||||||
feeFiatAmount: feeFiatAmount,
|
feeFiatAmount: feeFiatAmount,
|
||||||
outputs: outputs
|
outputs: outputs);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class ConfirmSendingAlertContentState extends State<ConfirmSendingAlertContent> {
|
class ConfirmSendingAlertContentState extends State<ConfirmSendingAlertContent> {
|
||||||
ConfirmSendingAlertContentState({
|
ConfirmSendingAlertContentState(
|
||||||
this.paymentId,
|
{this.paymentId,
|
||||||
this.paymentIdValue,
|
this.paymentIdValue,
|
||||||
required this.amount,
|
required this.amount,
|
||||||
required this.amountValue,
|
required this.amountValue,
|
||||||
required this.fiatAmountValue,
|
required this.fiatAmountValue,
|
||||||
required this.fee,
|
required this.fee,
|
||||||
required this.feeValue,
|
required this.feeValue,
|
||||||
required this.feeFiatAmount,
|
required this.feeFiatAmount,
|
||||||
required this.outputs})
|
required this.outputs})
|
||||||
: recipientTitle = '' {
|
: recipientTitle = '' {
|
||||||
recipientTitle = outputs.length > 1
|
recipientTitle = outputs.length > 1
|
||||||
? S.current.transaction_details_recipient_address
|
? S.current.transaction_details_recipient_address
|
||||||
|
@ -170,8 +168,9 @@ class ConfirmSendingAlertContentState extends State<ConfirmSendingAlertContent>
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
controller.addListener(() {
|
controller.addListener(() {
|
||||||
fromTop = controller.hasClients
|
fromTop = controller.hasClients
|
||||||
? (controller.offset / controller.position.maxScrollExtent *
|
? (controller.offset /
|
||||||
(backgroundHeight - thumbHeight))
|
controller.position.maxScrollExtent *
|
||||||
|
(backgroundHeight - thumbHeight))
|
||||||
: 0;
|
: 0;
|
||||||
setState(() {});
|
setState(() {});
|
||||||
});
|
});
|
||||||
|
@ -182,94 +181,92 @@ class ConfirmSendingAlertContentState extends State<ConfirmSendingAlertContent>
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
return Stack(
|
return Stack(alignment: Alignment.center, clipBehavior: Clip.none, children: [
|
||||||
alignment: Alignment.center,
|
Container(
|
||||||
clipBehavior: Clip.none,
|
height: 200,
|
||||||
children: [
|
child: SingleChildScrollView(
|
||||||
Container(
|
controller: controller,
|
||||||
height: 200,
|
child: Column(
|
||||||
child: SingleChildScrollView(
|
children: <Widget>[
|
||||||
controller: controller,
|
if (paymentIdValue != null && paymentId != null)
|
||||||
child: Column(
|
Padding(
|
||||||
children: <Widget>[
|
padding: EdgeInsets.only(bottom: 32),
|
||||||
if (paymentIdValue != null && paymentId != null)
|
child: Row(
|
||||||
Padding(
|
mainAxisSize: MainAxisSize.max,
|
||||||
padding: EdgeInsets.only(bottom: 32),
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
child: Row(
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
mainAxisSize: MainAxisSize.max,
|
children: <Widget>[
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
Text(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
paymentId!,
|
||||||
children: <Widget>[
|
style: TextStyle(
|
||||||
Text(
|
fontSize: 16,
|
||||||
paymentId!,
|
fontWeight: FontWeight.normal,
|
||||||
style: TextStyle(
|
fontFamily: 'Lato',
|
||||||
fontSize: 16,
|
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
|
||||||
fontWeight: FontWeight.normal,
|
decoration: TextDecoration.none,
|
||||||
fontFamily: 'Lato',
|
|
||||||
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
|
|
||||||
decoration: TextDecoration.none,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
Column(
|
),
|
||||||
crossAxisAlignment: CrossAxisAlignment.end,
|
Column(
|
||||||
children: [
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
Text(
|
children: [
|
||||||
paymentIdValue!,
|
Text(
|
||||||
style: TextStyle(
|
paymentIdValue!,
|
||||||
fontSize: 18,
|
style: TextStyle(
|
||||||
fontWeight: FontWeight.w600,
|
fontSize: 18,
|
||||||
fontFamily: 'Lato',
|
fontWeight: FontWeight.w600,
|
||||||
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
|
fontFamily: 'Lato',
|
||||||
decoration: TextDecoration.none,
|
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
|
||||||
),
|
decoration: TextDecoration.none,
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
)
|
],
|
||||||
],
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
mainAxisSize: MainAxisSize.max,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: <Widget>[
|
||||||
|
Text(
|
||||||
|
amount,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.normal,
|
||||||
|
fontFamily: 'Lato',
|
||||||
|
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
|
||||||
|
decoration: TextDecoration.none,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Row(
|
Column(
|
||||||
mainAxisSize: MainAxisSize.max,
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
children: [
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
Text(
|
||||||
children: <Widget>[
|
amountValue,
|
||||||
Text(
|
style: TextStyle(
|
||||||
amount,
|
fontSize: 18,
|
||||||
style: TextStyle(
|
fontWeight: FontWeight.w600,
|
||||||
fontSize: 16,
|
fontFamily: 'Lato',
|
||||||
fontWeight: FontWeight.normal,
|
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
|
||||||
fontFamily: 'Lato',
|
decoration: TextDecoration.none,
|
||||||
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
|
|
||||||
decoration: TextDecoration.none,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.end,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
amountValue,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 18,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
fontFamily: 'Lato',
|
|
||||||
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
|
|
||||||
decoration: TextDecoration.none,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
Text(
|
),
|
||||||
fiatAmountValue,
|
Text(
|
||||||
style: TextStyle(
|
fiatAmountValue,
|
||||||
fontSize: 12,
|
style: TextStyle(
|
||||||
fontWeight: FontWeight.w600,
|
fontSize: 12,
|
||||||
fontFamily: 'Lato',
|
fontWeight: FontWeight.w600,
|
||||||
color: PaletteDark.pigeonBlue,
|
fontFamily: 'Lato',
|
||||||
decoration: TextDecoration.none,
|
color: PaletteDark.pigeonBlue,
|
||||||
),
|
decoration: TextDecoration.none,
|
||||||
)
|
),
|
||||||
],
|
)
|
||||||
)
|
],
|
||||||
],
|
)
|
||||||
),
|
],
|
||||||
|
),
|
||||||
|
if (feeValue.isNotEmpty && feeValue != "0")
|
||||||
Padding(
|
Padding(
|
||||||
padding: EdgeInsets.only(top: 16),
|
padding: EdgeInsets.only(top: 16),
|
||||||
child: Row(
|
child: Row(
|
||||||
|
@ -313,103 +310,97 @@ class ConfirmSendingAlertContentState extends State<ConfirmSendingAlertContent>
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
)
|
)),
|
||||||
),
|
Padding(
|
||||||
Padding(
|
padding: EdgeInsets.only(top: 16),
|
||||||
padding: EdgeInsets.only(top: 16),
|
child: Column(
|
||||||
child: Column(
|
children: [
|
||||||
children: [
|
Text(
|
||||||
Text(
|
'$recipientTitle:',
|
||||||
'$recipientTitle:',
|
style: TextStyle(
|
||||||
style: TextStyle(
|
fontSize: 16,
|
||||||
fontSize: 16,
|
fontWeight: FontWeight.normal,
|
||||||
fontWeight: FontWeight.normal,
|
fontFamily: 'Lato',
|
||||||
fontFamily: 'Lato',
|
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
|
||||||
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
|
decoration: TextDecoration.none,
|
||||||
decoration: TextDecoration.none,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
outputs.length > 1
|
),
|
||||||
? ListView.builder(
|
outputs.length > 1
|
||||||
padding: EdgeInsets.only(top: 0),
|
? ListView.builder(
|
||||||
shrinkWrap: true,
|
padding: EdgeInsets.only(top: 0),
|
||||||
physics: NeverScrollableScrollPhysics(),
|
shrinkWrap: true,
|
||||||
itemCount: outputs.length,
|
physics: NeverScrollableScrollPhysics(),
|
||||||
itemBuilder: (context, index) {
|
itemCount: outputs.length,
|
||||||
final item = outputs[index];
|
itemBuilder: (context, index) {
|
||||||
final _address = item.isParsedAddress
|
final item = outputs[index];
|
||||||
? item.extractedAddress
|
final _address =
|
||||||
: item.address;
|
item.isParsedAddress ? item.extractedAddress : item.address;
|
||||||
final _amount =
|
final _amount = item.cryptoAmount.replaceAll(',', '.');
|
||||||
item.cryptoAmount.replaceAll(',', '.');
|
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: [
|
||||||
if (item.isParsedAddress) Padding(
|
if (item.isParsedAddress)
|
||||||
padding: EdgeInsets.only(top: 8),
|
Padding(
|
||||||
child: Text(
|
padding: EdgeInsets.only(top: 8),
|
||||||
item.parsedAddress.name,
|
child: Text(
|
||||||
textAlign: TextAlign.center,
|
item.parsedAddress.name,
|
||||||
style: TextStyle(
|
textAlign: TextAlign.center,
|
||||||
fontSize: 14,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
fontFamily: 'Lato',
|
|
||||||
color: PaletteDark.pigeonBlue,
|
|
||||||
decoration: TextDecoration.none,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding: EdgeInsets.only(top: 8),
|
|
||||||
child: Text(
|
|
||||||
_address,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 10,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
fontFamily: 'Lato',
|
|
||||||
color: PaletteDark.pigeonBlue,
|
|
||||||
decoration: TextDecoration.none,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding: EdgeInsets.only(top: 8),
|
|
||||||
child: Row(
|
|
||||||
mainAxisSize: MainAxisSize.max,
|
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
_amount,
|
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 10,
|
fontSize: 14,
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
fontFamily: 'Lato',
|
fontFamily: 'Lato',
|
||||||
color: PaletteDark.pigeonBlue,
|
color: PaletteDark.pigeonBlue,
|
||||||
decoration: TextDecoration.none,
|
decoration: TextDecoration.none,
|
||||||
),
|
),
|
||||||
)
|
)),
|
||||||
],
|
Padding(
|
||||||
)
|
padding: EdgeInsets.only(top: 8),
|
||||||
)
|
child: Text(
|
||||||
],
|
_address,
|
||||||
);
|
style: TextStyle(
|
||||||
})
|
fontSize: 10,
|
||||||
: Column(
|
fontWeight: FontWeight.w600,
|
||||||
children: [
|
fontFamily: 'Lato',
|
||||||
if (outputs.first.isParsedAddress) Padding(
|
color: PaletteDark.pigeonBlue,
|
||||||
padding: EdgeInsets.only(top: 8),
|
decoration: TextDecoration.none,
|
||||||
child: Text(
|
),
|
||||||
outputs.first.parsedAddress.name,
|
)),
|
||||||
textAlign: TextAlign.center,
|
Padding(
|
||||||
style: TextStyle(
|
padding: EdgeInsets.only(top: 8),
|
||||||
fontSize: 14,
|
child: Row(
|
||||||
fontWeight: FontWeight.w600,
|
mainAxisSize: MainAxisSize.max,
|
||||||
fontFamily: 'Lato',
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
color: PaletteDark.pigeonBlue,
|
children: [
|
||||||
decoration: TextDecoration.none,
|
Text(
|
||||||
),
|
_amount,
|
||||||
)
|
style: TextStyle(
|
||||||
),
|
fontSize: 10,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
fontFamily: 'Lato',
|
||||||
|
color: PaletteDark.pigeonBlue,
|
||||||
|
decoration: TextDecoration.none,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
))
|
||||||
|
],
|
||||||
|
);
|
||||||
|
})
|
||||||
|
: Column(children: [
|
||||||
|
if (outputs.first.isParsedAddress)
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.only(top: 8),
|
||||||
|
child: Text(
|
||||||
|
outputs.first.parsedAddress.name,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
fontFamily: 'Lato',
|
||||||
|
color: PaletteDark.pigeonBlue,
|
||||||
|
decoration: TextDecoration.none,
|
||||||
|
),
|
||||||
|
)),
|
||||||
Padding(
|
Padding(
|
||||||
padding: EdgeInsets.only(top: 8),
|
padding: EdgeInsets.only(top: 8),
|
||||||
child: Text(
|
child: Text(
|
||||||
|
@ -423,24 +414,19 @@ class ConfirmSendingAlertContentState extends State<ConfirmSendingAlertContent>
|
||||||
color: PaletteDark.pigeonBlue,
|
color: PaletteDark.pigeonBlue,
|
||||||
decoration: TextDecoration.none,
|
decoration: TextDecoration.none,
|
||||||
),
|
),
|
||||||
)
|
)),
|
||||||
),
|
])
|
||||||
]
|
],
|
||||||
)
|
),
|
||||||
],
|
)
|
||||||
),
|
],
|
||||||
)
|
))),
|
||||||
],
|
if (showScrollbar)
|
||||||
)
|
CakeScrollbar(
|
||||||
)
|
backgroundHeight: backgroundHeight,
|
||||||
),
|
thumbHeight: thumbHeight,
|
||||||
if (showScrollbar) CakeScrollbar(
|
fromTop: fromTop,
|
||||||
backgroundHeight: backgroundHeight,
|
rightOffset: -15)
|
||||||
thumbHeight: thumbHeight,
|
]);
|
||||||
fromTop: fromTop,
|
|
||||||
rightOffset: -15
|
|
||||||
)
|
|
||||||
]
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -167,8 +167,10 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
|
||||||
AddressTextFieldOption.qrCode,
|
AddressTextFieldOption.qrCode,
|
||||||
AddressTextFieldOption.addressBook
|
AddressTextFieldOption.addressBook
|
||||||
],
|
],
|
||||||
buttonColor: Theme.of(context).extension<SendPageTheme>()!.textFieldButtonColor,
|
buttonColor:
|
||||||
borderColor: Theme.of(context).extension<SendPageTheme>()!.textFieldBorderColor,
|
Theme.of(context).extension<SendPageTheme>()!.textFieldButtonColor,
|
||||||
|
borderColor:
|
||||||
|
Theme.of(context).extension<SendPageTheme>()!.textFieldBorderColor,
|
||||||
textStyle: TextStyle(
|
textStyle: TextStyle(
|
||||||
fontSize: 14, fontWeight: FontWeight.w500, color: Colors.white),
|
fontSize: 14, fontWeight: FontWeight.w500, color: Colors.white),
|
||||||
hintStyle: TextStyle(
|
hintStyle: TextStyle(
|
||||||
|
@ -196,7 +198,9 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
|
||||||
child: BaseTextFormField(
|
child: BaseTextFormField(
|
||||||
controller: extractedAddressController,
|
controller: extractedAddressController,
|
||||||
readOnly: true,
|
readOnly: true,
|
||||||
borderColor: Theme.of(context).extension<SendPageTheme>()!.textFieldBorderColor,
|
borderColor: Theme.of(context)
|
||||||
|
.extension<SendPageTheme>()!
|
||||||
|
.textFieldBorderColor,
|
||||||
textStyle: TextStyle(
|
textStyle: TextStyle(
|
||||||
fontSize: 14, fontWeight: FontWeight.w500, color: Colors.white),
|
fontSize: 14, fontWeight: FontWeight.w500, color: Colors.white),
|
||||||
validator: sendViewModel.addressValidator)),
|
validator: sendViewModel.addressValidator)),
|
||||||
|
@ -251,7 +255,9 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
|
||||||
child: Container(
|
child: Container(
|
||||||
height: 32,
|
height: 32,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Theme.of(context).extension<SendPageTheme>()!.textFieldButtonColor,
|
color: Theme.of(context)
|
||||||
|
.extension<SendPageTheme>()!
|
||||||
|
.textFieldButtonColor,
|
||||||
borderRadius: BorderRadius.all(
|
borderRadius: BorderRadius.all(
|
||||||
Radius.circular(6),
|
Radius.circular(6),
|
||||||
)),
|
)),
|
||||||
|
@ -263,7 +269,9 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
color: Theme.of(context).extension<SendPageTheme>()!.textFieldButtonIconColor),
|
color: Theme.of(context)
|
||||||
|
.extension<SendPageTheme>()!
|
||||||
|
.textFieldButtonIconColor),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -304,7 +312,9 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
color: Colors.white),
|
color: Colors.white),
|
||||||
placeholderTextStyle: TextStyle(
|
placeholderTextStyle: TextStyle(
|
||||||
color: Theme.of(context).extension<SendPageTheme>()!.textFieldHintColor,
|
color: Theme.of(context)
|
||||||
|
.extension<SendPageTheme>()!
|
||||||
|
.textFieldHintColor,
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
fontSize: 14),
|
fontSize: 14),
|
||||||
validator: output.sendAll
|
validator: output.sendAll
|
||||||
|
@ -322,7 +332,9 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
|
||||||
onTap: () async => output.setSendAll(),
|
onTap: () async => output.setSendAll(),
|
||||||
child: Container(
|
child: Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Theme.of(context).extension<SendPageTheme>()!.textFieldButtonColor,
|
color: Theme.of(context)
|
||||||
|
.extension<SendPageTheme>()!
|
||||||
|
.textFieldButtonColor,
|
||||||
borderRadius: BorderRadius.all(
|
borderRadius: BorderRadius.all(
|
||||||
Radius.circular(6),
|
Radius.circular(6),
|
||||||
),
|
),
|
||||||
|
@ -334,7 +346,9 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
color: Theme.of(context).extension<SendPageTheme>()!.textFieldButtonIconColor,
|
color: Theme.of(context)
|
||||||
|
.extension<SendPageTheme>()!
|
||||||
|
.textFieldButtonIconColor,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -364,7 +378,9 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
color: Theme.of(context).extension<SendPageTheme>()!.textFieldHintColor),
|
color: Theme.of(context)
|
||||||
|
.extension<SendPageTheme>()!
|
||||||
|
.textFieldHintColor),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
|
@ -372,7 +388,9 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
color: Theme.of(context).extension<SendPageTheme>()!.textFieldHintColor),
|
color: Theme.of(context)
|
||||||
|
.extension<SendPageTheme>()!
|
||||||
|
.textFieldHintColor),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -403,11 +421,13 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
hintText: '0.00',
|
hintText: '0.00',
|
||||||
borderColor: Theme.of(context).extension<SendPageTheme>()!.textFieldBorderColor,
|
borderColor:
|
||||||
|
Theme.of(context).extension<SendPageTheme>()!.textFieldBorderColor,
|
||||||
textStyle: TextStyle(
|
textStyle: TextStyle(
|
||||||
fontSize: 14, fontWeight: FontWeight.w500, color: Colors.white),
|
fontSize: 14, fontWeight: FontWeight.w500, color: Colors.white),
|
||||||
placeholderTextStyle: TextStyle(
|
placeholderTextStyle: TextStyle(
|
||||||
color: Theme.of(context).extension<SendPageTheme>()!.textFieldHintColor,
|
color:
|
||||||
|
Theme.of(context).extension<SendPageTheme>()!.textFieldHintColor,
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
fontSize: 14),
|
fontSize: 14),
|
||||||
),
|
),
|
||||||
|
@ -418,7 +438,8 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
|
||||||
controller: noteController,
|
controller: noteController,
|
||||||
keyboardType: TextInputType.multiline,
|
keyboardType: TextInputType.multiline,
|
||||||
maxLines: null,
|
maxLines: null,
|
||||||
borderColor: Theme.of(context).extension<SendPageTheme>()!.textFieldBorderColor,
|
borderColor:
|
||||||
|
Theme.of(context).extension<SendPageTheme>()!.textFieldBorderColor,
|
||||||
textStyle: TextStyle(
|
textStyle: TextStyle(
|
||||||
fontSize: 14, fontWeight: FontWeight.w500, color: Colors.white),
|
fontSize: 14, fontWeight: FontWeight.w500, color: Colors.white),
|
||||||
hintText: S.of(context).note_optional,
|
hintText: S.of(context).note_optional,
|
||||||
|
@ -429,73 +450,76 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
|
||||||
Theme.of(context).extension<SendPageTheme>()!.textFieldHintColor),
|
Theme.of(context).extension<SendPageTheme>()!.textFieldHintColor),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Observer(
|
if (sendViewModel.hasFees)
|
||||||
builder: (_) => GestureDetector(
|
Observer(
|
||||||
onTap: () => _setTransactionPriority(context),
|
builder: (_) => GestureDetector(
|
||||||
child: Container(
|
onTap: () => _setTransactionPriority(context),
|
||||||
padding: EdgeInsets.only(top: 24),
|
child: Container(
|
||||||
child: Row(
|
padding: EdgeInsets.only(top: 24),
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
child: Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: <Widget>[
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
Text(
|
children: <Widget>[
|
||||||
S.of(context).send_estimated_fee,
|
Text(
|
||||||
style: TextStyle(
|
S.of(context).send_estimated_fee,
|
||||||
fontSize: 12,
|
style: TextStyle(
|
||||||
fontWeight: FontWeight.w500,
|
fontSize: 12,
|
||||||
color: Colors.white),
|
fontWeight: FontWeight.w500,
|
||||||
),
|
color: Colors.white),
|
||||||
Container(
|
|
||||||
child: Row(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: <Widget>[
|
|
||||||
Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.end,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
output.estimatedFee.toString() +
|
|
||||||
' ' +
|
|
||||||
sendViewModel.selectedCryptoCurrency.toString(),
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 12,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
color: Colors.white,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding: EdgeInsets.only(top: 5),
|
|
||||||
child: sendViewModel.isFiatDisabled
|
|
||||||
? const SizedBox(height: 14)
|
|
||||||
: Text(
|
|
||||||
output.estimatedFeeFiatAmount +
|
|
||||||
' ' +
|
|
||||||
sendViewModel.fiat.title,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 12,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
color: Theme.of(context).extension<SendPageTheme>()!.textFieldHintColor,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding: EdgeInsets.only(top: 2, left: 5),
|
|
||||||
child: Icon(
|
|
||||||
Icons.arrow_forward_ios,
|
|
||||||
size: 12,
|
|
||||||
color: Colors.white,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
)
|
Container(
|
||||||
],
|
child: Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: <Widget>[
|
||||||
|
Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
output.estimatedFee.toString() +
|
||||||
|
' ' +
|
||||||
|
sendViewModel.selectedCryptoCurrency.toString(),
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.only(top: 5),
|
||||||
|
child: sendViewModel.isFiatDisabled
|
||||||
|
? const SizedBox(height: 14)
|
||||||
|
: Text(
|
||||||
|
output.estimatedFeeFiatAmount +
|
||||||
|
' ' +
|
||||||
|
sendViewModel.fiat.title,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color: Theme.of(context)
|
||||||
|
.extension<SendPageTheme>()!
|
||||||
|
.textFieldHintColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.only(top: 2, left: 5),
|
||||||
|
child: Icon(
|
||||||
|
Icons.arrow_forward_ios,
|
||||||
|
size: 12,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
if (sendViewModel.hasCoinControl)
|
if (sendViewModel.hasCoinControl)
|
||||||
Padding(
|
Padding(
|
||||||
padding: EdgeInsets.only(top: 6),
|
padding: EdgeInsets.only(top: 6),
|
||||||
|
|
|
@ -70,6 +70,21 @@ class ConnectionSyncPage extends BasePage {
|
||||||
handler: (context) => Navigator.of(context).pushNamed(Routes.manageNodes),
|
handler: (context) => Navigator.of(context).pushNamed(Routes.manageNodes),
|
||||||
),
|
),
|
||||||
const StandardListSeparator(padding: EdgeInsets.symmetric(horizontal: 24)),
|
const StandardListSeparator(padding: EdgeInsets.symmetric(horizontal: 24)),
|
||||||
|
Observer(
|
||||||
|
builder: (context) {
|
||||||
|
if (!dashboardViewModel.hasPowNodes) return const SizedBox();
|
||||||
|
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
SettingsCellWithArrow(
|
||||||
|
title: S.current.manage_pow_nodes,
|
||||||
|
handler: (context) => Navigator.of(context).pushNamed(Routes.managePowNodes),
|
||||||
|
),
|
||||||
|
const StandardListSeparator(padding: EdgeInsets.symmetric(horizontal: 24)),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
if (dashboardViewModel.wallet.type == WalletType.ethereum) ...[
|
if (dashboardViewModel.wallet.type == WalletType.ethereum) ...[
|
||||||
WalletConnectTile(
|
WalletConnectTile(
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
|
|
|
@ -6,13 +6,17 @@ import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
|
||||||
import 'package:cake_wallet/src/widgets/standard_list.dart';
|
import 'package:cake_wallet/src/widgets/standard_list.dart';
|
||||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||||
import 'package:cake_wallet/view_model/node_list/node_list_view_model.dart';
|
import 'package:cake_wallet/view_model/node_list/node_list_view_model.dart';
|
||||||
|
import 'package:cake_wallet/view_model/node_list/pow_node_list_view_model.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||||
|
|
||||||
class ManageNodesPage extends BasePage {
|
class ManageNodesPage extends BasePage {
|
||||||
ManageNodesPage(this.nodeListViewModel);
|
ManageNodesPage(this.isPow, {this.nodeListViewModel, this.powNodeListViewModel})
|
||||||
|
: assert((isPow && powNodeListViewModel != null) || (!isPow && nodeListViewModel != null));
|
||||||
|
|
||||||
final NodeListViewModel nodeListViewModel;
|
final NodeListViewModel? nodeListViewModel;
|
||||||
|
final PowNodeListViewModel? powNodeListViewModel;
|
||||||
|
final bool isPow;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get title => S.current.manage_nodes;
|
String get title => S.current.manage_nodes;
|
||||||
|
@ -34,7 +38,8 @@ class ManageNodesPage extends BasePage {
|
||||||
SizedBox(height: 20),
|
SizedBox(height: 20),
|
||||||
Observer(
|
Observer(
|
||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
int itemsCount = nodeListViewModel.nodes.length;
|
int itemsCount =
|
||||||
|
nodeListViewModel?.nodes.length ?? powNodeListViewModel!.nodes.length;
|
||||||
return Flexible(
|
return Flexible(
|
||||||
child: SectionStandardList(
|
child: SectionStandardList(
|
||||||
sectionCount: 1,
|
sectionCount: 1,
|
||||||
|
@ -43,12 +48,19 @@ class ManageNodesPage extends BasePage {
|
||||||
itemBuilder: (_, index) {
|
itemBuilder: (_, index) {
|
||||||
return Observer(
|
return Observer(
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
final node = nodeListViewModel.nodes[index];
|
final node =
|
||||||
final isSelected = node.keyIndex == nodeListViewModel.currentNode.keyIndex;
|
nodeListViewModel?.nodes[index] ?? powNodeListViewModel!.nodes[index];
|
||||||
|
late bool isSelected;
|
||||||
|
if (isPow) {
|
||||||
|
isSelected = node.keyIndex == powNodeListViewModel!.currentNode.keyIndex;
|
||||||
|
} else {
|
||||||
|
isSelected = node.keyIndex == nodeListViewModel!.currentNode.keyIndex;
|
||||||
|
}
|
||||||
final nodeListRow = NodeListRow(
|
final nodeListRow = NodeListRow(
|
||||||
title: node.uriRaw,
|
title: node.uriRaw,
|
||||||
node: node,
|
node: node,
|
||||||
isSelected: isSelected,
|
isSelected: isSelected,
|
||||||
|
isPow: false,
|
||||||
onTap: (_) async {
|
onTap: (_) async {
|
||||||
if (isSelected) {
|
if (isSelected) {
|
||||||
return;
|
return;
|
||||||
|
@ -59,12 +71,17 @@ class ManageNodesPage extends BasePage {
|
||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
return AlertWithTwoActions(
|
return AlertWithTwoActions(
|
||||||
alertTitle: S.of(context).change_current_node_title,
|
alertTitle: S.of(context).change_current_node_title,
|
||||||
alertContent: nodeListViewModel.getAlertContent(node.uriRaw),
|
alertContent: nodeListViewModel?.getAlertContent(node.uriRaw) ??
|
||||||
|
powNodeListViewModel!.getAlertContent(node.uriRaw),
|
||||||
leftButtonText: S.of(context).cancel,
|
leftButtonText: S.of(context).cancel,
|
||||||
rightButtonText: S.of(context).change,
|
rightButtonText: S.of(context).change,
|
||||||
actionLeftButton: () => Navigator.of(context).pop(),
|
actionLeftButton: () => Navigator.of(context).pop(),
|
||||||
actionRightButton: () async {
|
actionRightButton: () async {
|
||||||
await nodeListViewModel.setAsCurrent(node);
|
if (isPow) {
|
||||||
|
await powNodeListViewModel!.setAsCurrent(node);
|
||||||
|
} else {
|
||||||
|
await nodeListViewModel!.setAsCurrent(node);
|
||||||
|
}
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
|
@ -28,13 +28,20 @@ class OtherSettingsPage extends BasePage {
|
||||||
padding: EdgeInsets.only(top: 10),
|
padding: EdgeInsets.only(top: 10),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
SettingsPickerCell(
|
if (!_otherSettingsViewModel.changeRepresentativeEnabled)
|
||||||
title: S.current.settings_fee_priority,
|
SettingsPickerCell(
|
||||||
items: priorityForWalletType(_otherSettingsViewModel.walletType),
|
title: S.current.settings_fee_priority,
|
||||||
displayItem: _otherSettingsViewModel.getDisplayPriority,
|
items: priorityForWalletType(_otherSettingsViewModel.walletType),
|
||||||
selectedItem: _otherSettingsViewModel.transactionPriority,
|
displayItem: _otherSettingsViewModel.getDisplayPriority,
|
||||||
onItemSelected: _otherSettingsViewModel.onDisplayPrioritySelected,
|
selectedItem: _otherSettingsViewModel.transactionPriority,
|
||||||
),
|
onItemSelected: _otherSettingsViewModel.onDisplayPrioritySelected,
|
||||||
|
),
|
||||||
|
if (_otherSettingsViewModel.changeRepresentativeEnabled)
|
||||||
|
SettingsCellWithArrow(
|
||||||
|
title: S.current.change_rep,
|
||||||
|
handler: (BuildContext context) =>
|
||||||
|
Navigator.of(context).pushNamed(Routes.changeRep),
|
||||||
|
),
|
||||||
SettingsPickerCell(
|
SettingsPickerCell(
|
||||||
title: S.current.default_buy_provider,
|
title: S.current.default_buy_provider,
|
||||||
items: BuyProviderType.values,
|
items: BuyProviderType.values,
|
||||||
|
@ -50,7 +57,7 @@ class OtherSettingsPage extends BasePage {
|
||||||
StandardListSeparator(padding: EdgeInsets.symmetric(horizontal: 24)),
|
StandardListSeparator(padding: EdgeInsets.symmetric(horizontal: 24)),
|
||||||
Spacer(),
|
Spacer(),
|
||||||
SettingsVersionCell(
|
SettingsVersionCell(
|
||||||
title: S.of(context).version(_otherSettingsViewModel.currentVersion))
|
title: S.of(context).version(_otherSettingsViewModel.currentVersion)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -48,6 +48,7 @@ class WalletListBodyState extends State<WalletListBody> {
|
||||||
final nonWalletTypeIcon = Image.asset('assets/images/close.png', height: 24, width: 24);
|
final nonWalletTypeIcon = Image.asset('assets/images/close.png', height: 24, width: 24);
|
||||||
final havenIcon = Image.asset('assets/images/haven_logo.png', height: 24, width: 24);
|
final havenIcon = Image.asset('assets/images/haven_logo.png', height: 24, width: 24);
|
||||||
final ethereumIcon = Image.asset('assets/images/eth_icon.png', height: 24, width: 24);
|
final ethereumIcon = Image.asset('assets/images/eth_icon.png', height: 24, width: 24);
|
||||||
|
final nanoIcon = Image.asset('assets/images/nano_icon.png', height: 24, width: 24);
|
||||||
final scrollController = ScrollController();
|
final scrollController = ScrollController();
|
||||||
final double tileHeight = 60;
|
final double tileHeight = 60;
|
||||||
Flushbar<void>? _progressBar;
|
Flushbar<void>? _progressBar;
|
||||||
|
@ -242,6 +243,8 @@ class WalletListBodyState extends State<WalletListBody> {
|
||||||
return havenIcon;
|
return havenIcon;
|
||||||
case WalletType.ethereum:
|
case WalletType.ethereum:
|
||||||
return ethereumIcon;
|
return ethereumIcon;
|
||||||
|
case WalletType.nano:
|
||||||
|
return nanoIcon;
|
||||||
default:
|
default:
|
||||||
return nonWalletTypeIcon;
|
return nonWalletTypeIcon;
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,7 @@ class WelcomePage extends BasePage {
|
||||||
return S.of(context).haven_app_wallet_text;
|
return S.of(context).haven_app_wallet_text;
|
||||||
}
|
}
|
||||||
|
|
||||||
return S.of(context).first_wallet_text;
|
return S.of(context).new_first_wallet_text;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
|
@ -64,6 +64,7 @@ abstract class SettingsStoreBase with Store {
|
||||||
required this.appVersion,
|
required this.appVersion,
|
||||||
required this.deviceName,
|
required this.deviceName,
|
||||||
required Map<WalletType, Node> nodes,
|
required Map<WalletType, Node> nodes,
|
||||||
|
required Map<WalletType, Node> powNodes,
|
||||||
required this.shouldShowYatPopup,
|
required this.shouldShowYatPopup,
|
||||||
required this.isBitcoinBuyEnabled,
|
required this.isBitcoinBuyEnabled,
|
||||||
required this.actionlistDisplayMode,
|
required this.actionlistDisplayMode,
|
||||||
|
@ -86,6 +87,7 @@ abstract class SettingsStoreBase with Store {
|
||||||
TransactionPriority? initialLitecoinTransactionPriority,
|
TransactionPriority? initialLitecoinTransactionPriority,
|
||||||
TransactionPriority? initialEthereumTransactionPriority})
|
TransactionPriority? initialEthereumTransactionPriority})
|
||||||
: nodes = ObservableMap<WalletType, Node>.of(nodes),
|
: nodes = ObservableMap<WalletType, Node>.of(nodes),
|
||||||
|
powNodes = ObservableMap<WalletType, Node>.of(powNodes),
|
||||||
_sharedPreferences = sharedPreferences,
|
_sharedPreferences = sharedPreferences,
|
||||||
_backgroundTasks = backgroundTasks,
|
_backgroundTasks = backgroundTasks,
|
||||||
fiatCurrency = initialFiatCurrency,
|
fiatCurrency = initialFiatCurrency,
|
||||||
|
@ -347,6 +349,12 @@ abstract class SettingsStoreBase with Store {
|
||||||
_saveCurrentNode(change.newValue!, change.key!);
|
_saveCurrentNode(change.newValue!, change.key!);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.powNodes.observe((change) {
|
||||||
|
if (change.newValue != null && change.key != null) {
|
||||||
|
_saveCurrentPowNode(change.newValue!, change.key!);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static const defaultPinLength = 4;
|
static const defaultPinLength = 4;
|
||||||
|
@ -473,6 +481,7 @@ abstract class SettingsStoreBase with Store {
|
||||||
final BackgroundTasks _backgroundTasks;
|
final BackgroundTasks _backgroundTasks;
|
||||||
|
|
||||||
ObservableMap<WalletType, Node> nodes;
|
ObservableMap<WalletType, Node> nodes;
|
||||||
|
ObservableMap<WalletType, Node> powNodes;
|
||||||
|
|
||||||
Node getCurrentNode(WalletType walletType) {
|
Node getCurrentNode(WalletType walletType) {
|
||||||
final node = nodes[walletType];
|
final node = nodes[walletType];
|
||||||
|
@ -484,6 +493,16 @@ abstract class SettingsStoreBase with Store {
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Node getCurrentPowNode(WalletType walletType) {
|
||||||
|
final node = powNodes[walletType];
|
||||||
|
|
||||||
|
if (node == null) {
|
||||||
|
throw Exception('No pow node found for wallet type: ${walletType.toString()}');
|
||||||
|
}
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
bool isBitcoinBuyEnabled;
|
bool isBitcoinBuyEnabled;
|
||||||
|
|
||||||
bool get shouldShowReceiveWarning =>
|
bool get shouldShowReceiveWarning =>
|
||||||
|
@ -494,6 +513,7 @@ abstract class SettingsStoreBase with Store {
|
||||||
|
|
||||||
static Future<SettingsStore> load(
|
static Future<SettingsStore> load(
|
||||||
{required Box<Node> nodeSource,
|
{required Box<Node> nodeSource,
|
||||||
|
required Box<Node> powNodeSource,
|
||||||
required bool isBitcoinBuyEnabled,
|
required bool isBitcoinBuyEnabled,
|
||||||
FiatCurrency initialFiatCurrency = FiatCurrency.usd,
|
FiatCurrency initialFiatCurrency = FiatCurrency.usd,
|
||||||
BalanceDisplayMode initialBalanceDisplayMode = BalanceDisplayMode.availableBalance,
|
BalanceDisplayMode initialBalanceDisplayMode = BalanceDisplayMode.availableBalance,
|
||||||
|
@ -611,11 +631,15 @@ abstract class SettingsStoreBase with Store {
|
||||||
sharedPreferences.getInt(PreferencesKey.currentLitecoinElectrumSererIdKey);
|
sharedPreferences.getInt(PreferencesKey.currentLitecoinElectrumSererIdKey);
|
||||||
final havenNodeId = sharedPreferences.getInt(PreferencesKey.currentHavenNodeIdKey);
|
final havenNodeId = sharedPreferences.getInt(PreferencesKey.currentHavenNodeIdKey);
|
||||||
final ethereumNodeId = sharedPreferences.getInt(PreferencesKey.currentEthereumNodeIdKey);
|
final ethereumNodeId = sharedPreferences.getInt(PreferencesKey.currentEthereumNodeIdKey);
|
||||||
|
final nanoNodeId = sharedPreferences.getInt(PreferencesKey.currentNanoNodeIdKey);
|
||||||
|
final nanoPowNodeId = sharedPreferences.getInt(PreferencesKey.currentNanoPowNodeIdKey);
|
||||||
final moneroNode = nodeSource.get(nodeId);
|
final moneroNode = nodeSource.get(nodeId);
|
||||||
final bitcoinElectrumServer = nodeSource.get(bitcoinElectrumServerId);
|
final bitcoinElectrumServer = nodeSource.get(bitcoinElectrumServerId);
|
||||||
final litecoinElectrumServer = nodeSource.get(litecoinElectrumServerId);
|
final litecoinElectrumServer = nodeSource.get(litecoinElectrumServerId);
|
||||||
final havenNode = nodeSource.get(havenNodeId);
|
final havenNode = nodeSource.get(havenNodeId);
|
||||||
final ethereumNode = nodeSource.get(ethereumNodeId);
|
final ethereumNode = nodeSource.get(ethereumNodeId);
|
||||||
|
final nanoNode = nodeSource.get(nanoNodeId);
|
||||||
|
final nanoPowNode = powNodeSource.get(nanoPowNodeId);
|
||||||
final packageInfo = await PackageInfo.fromPlatform();
|
final packageInfo = await PackageInfo.fromPlatform();
|
||||||
final deviceName = await _getDeviceName() ?? '';
|
final deviceName = await _getDeviceName() ?? '';
|
||||||
final shouldShowYatPopup = sharedPreferences.getBool(PreferencesKey.shouldShowYatPopup) ?? true;
|
final shouldShowYatPopup = sharedPreferences.getBool(PreferencesKey.shouldShowYatPopup) ?? true;
|
||||||
|
@ -626,6 +650,7 @@ abstract class SettingsStoreBase with Store {
|
||||||
? AutoGenerateSubaddressStatus.deserialize(raw: generateSubaddresses)
|
? AutoGenerateSubaddressStatus.deserialize(raw: generateSubaddresses)
|
||||||
: defaultAutoGenerateSubaddressStatus;
|
: defaultAutoGenerateSubaddressStatus;
|
||||||
final nodes = <WalletType, Node>{};
|
final nodes = <WalletType, Node>{};
|
||||||
|
final powNodes = <WalletType, Node>{};
|
||||||
|
|
||||||
if (moneroNode != null) {
|
if (moneroNode != null) {
|
||||||
nodes[WalletType.monero] = moneroNode;
|
nodes[WalletType.monero] = moneroNode;
|
||||||
|
@ -647,6 +672,13 @@ abstract class SettingsStoreBase with Store {
|
||||||
nodes[WalletType.ethereum] = ethereumNode;
|
nodes[WalletType.ethereum] = ethereumNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (nanoNode != null) {
|
||||||
|
nodes[WalletType.nano] = nanoNode;
|
||||||
|
}
|
||||||
|
if (nanoPowNode != null) {
|
||||||
|
powNodes[WalletType.nano] = nanoPowNode;
|
||||||
|
}
|
||||||
|
|
||||||
final savedSyncMode = SyncMode.all.firstWhere((element) {
|
final savedSyncMode = SyncMode.all.firstWhere((element) {
|
||||||
return element.type.index == (sharedPreferences.getInt(PreferencesKey.syncModeKey) ?? 1);
|
return element.type.index == (sharedPreferences.getInt(PreferencesKey.syncModeKey) ?? 1);
|
||||||
});
|
});
|
||||||
|
@ -656,6 +688,7 @@ abstract class SettingsStoreBase with Store {
|
||||||
sharedPreferences: sharedPreferences,
|
sharedPreferences: sharedPreferences,
|
||||||
initialShouldShowMarketPlaceInDashboard: shouldShowMarketPlaceInDashboard,
|
initialShouldShowMarketPlaceInDashboard: shouldShowMarketPlaceInDashboard,
|
||||||
nodes: nodes,
|
nodes: nodes,
|
||||||
|
powNodes: powNodes,
|
||||||
appVersion: packageInfo.version,
|
appVersion: packageInfo.version,
|
||||||
deviceName: deviceName,
|
deviceName: deviceName,
|
||||||
isBitcoinBuyEnabled: isBitcoinBuyEnabled,
|
isBitcoinBuyEnabled: isBitcoinBuyEnabled,
|
||||||
|
@ -818,11 +851,14 @@ abstract class SettingsStoreBase with Store {
|
||||||
sharedPreferences.getInt(PreferencesKey.currentLitecoinElectrumSererIdKey);
|
sharedPreferences.getInt(PreferencesKey.currentLitecoinElectrumSererIdKey);
|
||||||
final havenNodeId = sharedPreferences.getInt(PreferencesKey.currentHavenNodeIdKey);
|
final havenNodeId = sharedPreferences.getInt(PreferencesKey.currentHavenNodeIdKey);
|
||||||
final ethereumNodeId = sharedPreferences.getInt(PreferencesKey.currentEthereumNodeIdKey);
|
final ethereumNodeId = sharedPreferences.getInt(PreferencesKey.currentEthereumNodeIdKey);
|
||||||
|
final nanoNodeId = sharedPreferences.getInt(PreferencesKey.currentNanoNodeIdKey);
|
||||||
|
final nanoPowNodeId = sharedPreferences.getInt(PreferencesKey.currentNanoNodeIdKey);
|
||||||
final moneroNode = nodeSource.get(nodeId);
|
final moneroNode = nodeSource.get(nodeId);
|
||||||
final bitcoinElectrumServer = nodeSource.get(bitcoinElectrumServerId);
|
final bitcoinElectrumServer = nodeSource.get(bitcoinElectrumServerId);
|
||||||
final litecoinElectrumServer = nodeSource.get(litecoinElectrumServerId);
|
final litecoinElectrumServer = nodeSource.get(litecoinElectrumServerId);
|
||||||
final havenNode = nodeSource.get(havenNodeId);
|
final havenNode = nodeSource.get(havenNodeId);
|
||||||
final ethereumNode = nodeSource.get(ethereumNodeId);
|
final ethereumNode = nodeSource.get(ethereumNodeId);
|
||||||
|
final nanoNode = nodeSource.get(nanoNodeId);
|
||||||
|
|
||||||
if (moneroNode != null) {
|
if (moneroNode != null) {
|
||||||
nodes[WalletType.monero] = moneroNode;
|
nodes[WalletType.monero] = moneroNode;
|
||||||
|
@ -843,6 +879,10 @@ abstract class SettingsStoreBase with Store {
|
||||||
if (ethereumNode != null) {
|
if (ethereumNode != null) {
|
||||||
nodes[WalletType.ethereum] = ethereumNode;
|
nodes[WalletType.ethereum] = ethereumNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (nanoNode != null) {
|
||||||
|
nodes[WalletType.nano] = nanoNode;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _saveCurrentNode(Node node, WalletType walletType) async {
|
Future<void> _saveCurrentNode(Node node, WalletType walletType) async {
|
||||||
|
@ -864,6 +904,9 @@ abstract class SettingsStoreBase with Store {
|
||||||
case WalletType.ethereum:
|
case WalletType.ethereum:
|
||||||
await _sharedPreferences.setInt(PreferencesKey.currentEthereumNodeIdKey, node.key as int);
|
await _sharedPreferences.setInt(PreferencesKey.currentEthereumNodeIdKey, node.key as int);
|
||||||
break;
|
break;
|
||||||
|
case WalletType.nano:
|
||||||
|
await _sharedPreferences.setInt(PreferencesKey.currentNanoNodeIdKey, node.key as int);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -871,6 +914,18 @@ abstract class SettingsStoreBase with Store {
|
||||||
nodes[walletType] = node;
|
nodes[walletType] = node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _saveCurrentPowNode(Node node, WalletType walletType) async {
|
||||||
|
switch (walletType) {
|
||||||
|
case WalletType.nano:
|
||||||
|
await _sharedPreferences.setInt(PreferencesKey.currentNanoPowNodeIdKey, node.key as int);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
powNodes[walletType] = node;
|
||||||
|
}
|
||||||
|
|
||||||
static Future<String?> _getDeviceName() async {
|
static Future<String?> _getDeviceName() async {
|
||||||
String? deviceName = '';
|
String? deviceName = '';
|
||||||
final deviceInfoPlugin = DeviceInfoPlugin();
|
final deviceInfoPlugin = DeviceInfoPlugin();
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import 'package:cake_wallet/nano/nano.dart';
|
||||||
|
|
||||||
class PaymentRequest {
|
class PaymentRequest {
|
||||||
PaymentRequest(this.address, this.amount, this.note, this.scheme);
|
PaymentRequest(this.address, this.amount, this.note, this.scheme);
|
||||||
|
|
||||||
|
@ -10,11 +12,18 @@ class PaymentRequest {
|
||||||
if (uri != null) {
|
if (uri != null) {
|
||||||
address = uri.path;
|
address = uri.path;
|
||||||
amount = uri.queryParameters['tx_amount'] ?? uri.queryParameters['amount'] ?? "";
|
amount = uri.queryParameters['tx_amount'] ?? uri.queryParameters['amount'] ?? "";
|
||||||
note = uri.queryParameters['tx_description']
|
note = uri.queryParameters['tx_description'] ?? uri.queryParameters['message'] ?? "";
|
||||||
?? uri.queryParameters['message'] ?? "";
|
|
||||||
scheme = uri.scheme;
|
scheme = uri.scheme;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (nano != null) {
|
||||||
|
if (address.contains("nano")) {
|
||||||
|
amount = nanoUtil!.getRawAsUsableString(amount, nanoUtil!.rawPerNano);
|
||||||
|
} else if (address.contains("ban")) {
|
||||||
|
amount = nanoUtil!.getRawAsUsableString(amount, nanoUtil!.rawPerBanano);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return PaymentRequest(address, amount, note, scheme);
|
return PaymentRequest(address, amount, note, scheme);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,12 +15,11 @@ import 'package:collection/collection.dart';
|
||||||
|
|
||||||
part 'contact_list_view_model.g.dart';
|
part 'contact_list_view_model.g.dart';
|
||||||
|
|
||||||
class ContactListViewModel = ContactListViewModelBase
|
class ContactListViewModel = ContactListViewModelBase with _$ContactListViewModel;
|
||||||
with _$ContactListViewModel;
|
|
||||||
|
|
||||||
abstract class ContactListViewModelBase with Store {
|
abstract class ContactListViewModelBase with Store {
|
||||||
ContactListViewModelBase(this.contactSource, this.walletInfoSource,
|
ContactListViewModelBase(
|
||||||
this._currency, this.settingsStore)
|
this.contactSource, this.walletInfoSource, this._currency, this.settingsStore)
|
||||||
: contacts = ObservableList<ContactRecord>(),
|
: contacts = ObservableList<ContactRecord>(),
|
||||||
walletContacts = [],
|
walletContacts = [],
|
||||||
isAutoGenerateEnabled =
|
isAutoGenerateEnabled =
|
||||||
|
@ -48,6 +47,12 @@ abstract class ContactListViewModelBase with Store {
|
||||||
walletTypeToCryptoCurrency(info.type),
|
walletTypeToCryptoCurrency(info.type),
|
||||||
));
|
));
|
||||||
});
|
});
|
||||||
|
} else if (info.address != null) {
|
||||||
|
walletContacts.add(WalletContact(
|
||||||
|
info.address,
|
||||||
|
info.name,
|
||||||
|
walletTypeToCryptoCurrency(info.type),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -78,14 +83,12 @@ abstract class ContactListViewModelBase with Store {
|
||||||
Future<void> delete(ContactRecord contact) async => contact.original.delete();
|
Future<void> delete(ContactRecord contact) async => contact.original.delete();
|
||||||
|
|
||||||
@computed
|
@computed
|
||||||
List<ContactRecord> get contactsToShow => contacts
|
List<ContactRecord> get contactsToShow =>
|
||||||
.where((element) => _isValidForCurrency(element))
|
contacts.where((element) => _isValidForCurrency(element)).toList();
|
||||||
.toList();
|
|
||||||
|
|
||||||
@computed
|
@computed
|
||||||
List<WalletContact> get walletContactsToShow => walletContacts
|
List<WalletContact> get walletContactsToShow =>
|
||||||
.where((element) => _isValidForCurrency(element))
|
walletContacts.where((element) => _isValidForCurrency(element)).toList();
|
||||||
.toList();
|
|
||||||
|
|
||||||
bool _isValidForCurrency(ContactBase element) {
|
bool _isValidForCurrency(ContactBase element) {
|
||||||
return _currency == null || element.type == _currency || element.type.title == _currency!.tag;
|
return _currency == null || element.type == _currency || element.type.title == _currency!.tag;
|
||||||
|
|
|
@ -47,74 +47,71 @@ abstract class DashboardViewModelBase with Store {
|
||||||
required this.yatStore,
|
required this.yatStore,
|
||||||
required this.ordersStore,
|
required this.ordersStore,
|
||||||
required this.anonpayTransactionsStore})
|
required this.anonpayTransactionsStore})
|
||||||
: isOutdatedElectrumWallet = false,
|
: hasSellAction = false,
|
||||||
hasSellAction = false,
|
hasBuyAction = false,
|
||||||
hasBuyAction = false,
|
hasExchangeAction = false,
|
||||||
hasExchangeAction = false,
|
isShowFirstYatIntroduction = false,
|
||||||
isShowFirstYatIntroduction = false,
|
isShowSecondYatIntroduction = false,
|
||||||
isShowSecondYatIntroduction = false,
|
isShowThirdYatIntroduction = false,
|
||||||
isShowThirdYatIntroduction = false,
|
filterItems = {
|
||||||
filterItems = {
|
S.current.transactions: [
|
||||||
S.current.transactions: [
|
FilterItem(
|
||||||
FilterItem(
|
value: () => transactionFilterStore.displayAll,
|
||||||
value: () => transactionFilterStore.displayAll,
|
caption: S.current.all_transactions,
|
||||||
caption: S.current.all_transactions,
|
onChanged: transactionFilterStore.toggleAll),
|
||||||
onChanged: transactionFilterStore.toggleAll),
|
FilterItem(
|
||||||
FilterItem(
|
value: () => transactionFilterStore.displayIncoming,
|
||||||
value: () => transactionFilterStore.displayIncoming,
|
caption: S.current.incoming,
|
||||||
caption: S.current.incoming,
|
onChanged:transactionFilterStore.toggleIncoming),
|
||||||
onChanged: transactionFilterStore.toggleIncoming),
|
FilterItem(
|
||||||
FilterItem(
|
value: () => transactionFilterStore.displayOutgoing,
|
||||||
value: () => transactionFilterStore.displayOutgoing,
|
caption: S.current.outgoing,
|
||||||
caption: S.current.outgoing,
|
onChanged: transactionFilterStore.toggleOutgoing),
|
||||||
onChanged: transactionFilterStore.toggleOutgoing),
|
// FilterItem(
|
||||||
// FilterItem(
|
// value: () => false,
|
||||||
// value: () => false,
|
// caption: S.current.transactions_by_date,
|
||||||
// caption: S.current.transactions_by_date,
|
// onChanged: null),
|
||||||
// onChanged: null),
|
],
|
||||||
],
|
S.current.trades: [
|
||||||
S.current.trades: [
|
FilterItem(
|
||||||
FilterItem(
|
value: () => tradeFilterStore.displayAllTrades,
|
||||||
value: () => tradeFilterStore.displayAllTrades,
|
caption: S.current.all_trades,
|
||||||
caption: S.current.all_trades,
|
onChanged: () => tradeFilterStore
|
||||||
onChanged: () =>
|
.toggleDisplayExchange(ExchangeProviderDescription.all)),
|
||||||
tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.all)),
|
FilterItem(
|
||||||
FilterItem(
|
value: () => tradeFilterStore.displayChangeNow,
|
||||||
value: () => tradeFilterStore.displayChangeNow,
|
caption: ExchangeProviderDescription.changeNow.title,
|
||||||
caption: ExchangeProviderDescription.changeNow.title,
|
onChanged: () => tradeFilterStore
|
||||||
onChanged: () =>
|
.toggleDisplayExchange(ExchangeProviderDescription.changeNow)),
|
||||||
tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.changeNow)),
|
FilterItem(
|
||||||
FilterItem(
|
value: () => tradeFilterStore.displaySideShift,
|
||||||
value: () => tradeFilterStore.displaySideShift,
|
caption: ExchangeProviderDescription.sideShift.title,
|
||||||
caption: ExchangeProviderDescription.sideShift.title,
|
onChanged: () => tradeFilterStore
|
||||||
onChanged: () =>
|
.toggleDisplayExchange(ExchangeProviderDescription.sideShift)),
|
||||||
tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.sideShift)),
|
FilterItem(
|
||||||
FilterItem(
|
value: () => tradeFilterStore.displaySimpleSwap,
|
||||||
value: () => tradeFilterStore.displaySimpleSwap,
|
caption: ExchangeProviderDescription.simpleSwap.title,
|
||||||
caption: ExchangeProviderDescription.simpleSwap.title,
|
onChanged: () => tradeFilterStore
|
||||||
onChanged: () =>
|
.toggleDisplayExchange(ExchangeProviderDescription.simpleSwap)),
|
||||||
tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.simpleSwap)),
|
FilterItem(
|
||||||
FilterItem(
|
value: () => tradeFilterStore.displayTrocador,
|
||||||
value: () => tradeFilterStore.displayTrocador,
|
caption: ExchangeProviderDescription.trocador.title,
|
||||||
caption: ExchangeProviderDescription.trocador.title,
|
onChanged: () => tradeFilterStore
|
||||||
onChanged: () =>
|
.toggleDisplayExchange(ExchangeProviderDescription.trocador)),
|
||||||
tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.trocador)),
|
FilterItem(
|
||||||
FilterItem(
|
value: () => tradeFilterStore.displayExolix,
|
||||||
value: () => tradeFilterStore.displayExolix,
|
caption: ExchangeProviderDescription.exolix.title,
|
||||||
caption: ExchangeProviderDescription.exolix.title,
|
onChanged: () => tradeFilterStore
|
||||||
onChanged: () =>
|
.toggleDisplayExchange(ExchangeProviderDescription.exolix)),
|
||||||
tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.exolix)),
|
]
|
||||||
]
|
},
|
||||||
},
|
subname = '',
|
||||||
subname = '',
|
name = appStore.wallet!.name,
|
||||||
name = appStore.wallet!.name,
|
type = appStore.wallet!.type,
|
||||||
type = appStore.wallet!.type,
|
transactions = ObservableList<TransactionListItem>(),
|
||||||
transactions = ObservableList<TransactionListItem>(),
|
wallet = appStore.wallet! {
|
||||||
wallet = appStore.wallet! {
|
|
||||||
name = wallet.name;
|
name = wallet.name;
|
||||||
type = wallet.type;
|
type = wallet.type;
|
||||||
isOutdatedElectrumWallet =
|
|
||||||
wallet.type == WalletType.bitcoin && wallet.seed!.split(' ').length < 24;
|
|
||||||
isShowFirstYatIntroduction = false;
|
isShowFirstYatIntroduction = false;
|
||||||
isShowSecondYatIntroduction = false;
|
isShowSecondYatIntroduction = false;
|
||||||
isShowThirdYatIntroduction = false;
|
isShowThirdYatIntroduction = false;
|
||||||
|
@ -151,6 +148,11 @@ abstract class DashboardViewModelBase with Store {
|
||||||
settingsStore: appStore.settingsStore)));
|
settingsStore: appStore.settingsStore)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: nano sub-account generation is disabled:
|
||||||
|
// if (_wallet.type == WalletType.nano || _wallet.type == WalletType.banano) {
|
||||||
|
// subname = nano!.getCurrentAccount(_wallet).label;
|
||||||
|
// }
|
||||||
|
|
||||||
reaction((_) => appStore.wallet, _onWalletChange);
|
reaction((_) => appStore.wallet, _onWalletChange);
|
||||||
|
|
||||||
connectMapToListWithTransform(
|
connectMapToListWithTransform(
|
||||||
|
@ -315,12 +317,16 @@ abstract class DashboardViewModelBase with Store {
|
||||||
|
|
||||||
ReactionDisposer? _onMoneroBalanceChangeReaction;
|
ReactionDisposer? _onMoneroBalanceChangeReaction;
|
||||||
|
|
||||||
@observable
|
@computed
|
||||||
bool isOutdatedElectrumWallet;
|
bool get hasPowNodes => wallet.type == WalletType.nano || wallet.type == WalletType.banano;
|
||||||
|
|
||||||
Future<void> reconnect() async {
|
Future<void> reconnect() async {
|
||||||
final node = appStore.settingsStore.getCurrentNode(wallet.type);
|
final node = appStore.settingsStore.getCurrentNode(wallet.type);
|
||||||
await wallet.connectToNode(node: node);
|
await wallet.connectToNode(node: node);
|
||||||
|
if (hasPowNodes) {
|
||||||
|
final powNode = settingsStore.getCurrentPowNode(wallet.type);
|
||||||
|
await wallet.connectToPowNode(node: powNode);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
|
@ -333,8 +339,6 @@ abstract class DashboardViewModelBase with Store {
|
||||||
this.wallet = wallet;
|
this.wallet = wallet;
|
||||||
type = wallet.type;
|
type = wallet.type;
|
||||||
name = wallet.name;
|
name = wallet.name;
|
||||||
isOutdatedElectrumWallet =
|
|
||||||
wallet.type == WalletType.bitcoin && wallet.seed!.split(' ').length < 24;
|
|
||||||
updateActions();
|
updateActions();
|
||||||
|
|
||||||
if (wallet.type == WalletType.monero) {
|
if (wallet.type == WalletType.monero) {
|
||||||
|
|
|
@ -2,6 +2,7 @@ import 'package:cake_wallet/entities/balance_display_mode.dart';
|
||||||
import 'package:cake_wallet/entities/fiat_currency.dart';
|
import 'package:cake_wallet/entities/fiat_currency.dart';
|
||||||
import 'package:cake_wallet/ethereum/ethereum.dart';
|
import 'package:cake_wallet/ethereum/ethereum.dart';
|
||||||
import 'package:cake_wallet/generated/i18n.dart';
|
import 'package:cake_wallet/generated/i18n.dart';
|
||||||
|
import 'package:cake_wallet/nano/nano.dart';
|
||||||
import 'package:cw_core/transaction_direction.dart';
|
import 'package:cw_core/transaction_direction.dart';
|
||||||
import 'package:cw_core/transaction_info.dart';
|
import 'package:cw_core/transaction_info.dart';
|
||||||
import 'package:cake_wallet/store/settings_store.dart';
|
import 'package:cake_wallet/store/settings_store.dart';
|
||||||
|
@ -16,9 +17,7 @@ import 'package:cw_core/wallet_type.dart';
|
||||||
|
|
||||||
class TransactionListItem extends ActionListItem with Keyable {
|
class TransactionListItem extends ActionListItem with Keyable {
|
||||||
TransactionListItem(
|
TransactionListItem(
|
||||||
{required this.transaction,
|
{required this.transaction, required this.balanceViewModel, required this.settingsStore});
|
||||||
required this.balanceViewModel,
|
|
||||||
required this.settingsStore});
|
|
||||||
|
|
||||||
final TransactionInfo transaction;
|
final TransactionInfo transaction;
|
||||||
final BalanceViewModel balanceViewModel;
|
final BalanceViewModel balanceViewModel;
|
||||||
|
@ -34,10 +33,9 @@ class TransactionListItem extends ActionListItem with Keyable {
|
||||||
dynamic get keyIndex => transaction.id;
|
dynamic get keyIndex => transaction.id;
|
||||||
|
|
||||||
String get formattedCryptoAmount {
|
String get formattedCryptoAmount {
|
||||||
return displayMode == BalanceDisplayMode.hiddenBalance
|
return displayMode == BalanceDisplayMode.hiddenBalance ? '---' : transaction.amountFormatted();
|
||||||
? '---'
|
|
||||||
: transaction.amountFormatted();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
String get formattedTitle {
|
String get formattedTitle {
|
||||||
if (transaction.direction == TransactionDirection.incoming) {
|
if (transaction.direction == TransactionDirection.incoming) {
|
||||||
return S.current.received;
|
return S.current.received;
|
||||||
|
@ -57,33 +55,47 @@ class TransactionListItem extends ActionListItem with Keyable {
|
||||||
if (transaction.direction == TransactionDirection.incoming) {
|
if (transaction.direction == TransactionDirection.incoming) {
|
||||||
if (balanceViewModel.wallet.type == WalletType.monero ||
|
if (balanceViewModel.wallet.type == WalletType.monero ||
|
||||||
balanceViewModel.wallet.type == WalletType.haven) {
|
balanceViewModel.wallet.type == WalletType.haven) {
|
||||||
return formattedPendingStatus;
|
return formattedPendingStatus;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return transaction.isPending ? S.current.pending : '';
|
|
||||||
}
|
}
|
||||||
|
return transaction.isPending ? S.current.pending : '';
|
||||||
|
}
|
||||||
|
|
||||||
String get formattedFiatAmount {
|
String get formattedFiatAmount {
|
||||||
var amount = '';
|
var amount = '';
|
||||||
|
|
||||||
switch(balanceViewModel.wallet.type) {
|
switch (balanceViewModel.wallet.type) {
|
||||||
case WalletType.monero:
|
case WalletType.monero:
|
||||||
amount = calculateFiatAmountRaw(
|
amount = calculateFiatAmountRaw(
|
||||||
cryptoAmount: monero!.formatterMoneroAmountToDouble(amount: transaction.amount),
|
cryptoAmount: monero!.formatterMoneroAmountToDouble(amount: transaction.amount),
|
||||||
price: price);
|
price: price);
|
||||||
break;
|
break;
|
||||||
case WalletType.bitcoin:
|
case WalletType.bitcoin:
|
||||||
case WalletType.litecoin:
|
case WalletType.litecoin:
|
||||||
amount = calculateFiatAmountRaw(
|
amount = calculateFiatAmountRaw(
|
||||||
cryptoAmount: bitcoin!.formatterBitcoinAmountToDouble(amount: transaction.amount),
|
cryptoAmount: bitcoin!.formatterBitcoinAmountToDouble(amount: transaction.amount),
|
||||||
price: price);
|
price: price);
|
||||||
break;
|
break;
|
||||||
case WalletType.haven:
|
case WalletType.haven:
|
||||||
final asset = haven!.assetOfTransaction(transaction);
|
final asset = haven!.assetOfTransaction(transaction);
|
||||||
final price = balanceViewModel.fiatConvertationStore.prices[asset];
|
final price = balanceViewModel.fiatConvertationStore.prices[asset];
|
||||||
amount = calculateFiatAmountRaw(
|
amount = calculateFiatAmountRaw(
|
||||||
cryptoAmount: haven!.formatterMoneroAmountToDouble(amount: transaction.amount),
|
cryptoAmount: haven!.formatterMoneroAmountToDouble(amount: transaction.amount),
|
||||||
price: price);
|
price: price);
|
||||||
|
break;
|
||||||
|
case WalletType.ethereum:
|
||||||
|
final asset = ethereum!.assetOfTransaction(balanceViewModel.wallet, transaction);
|
||||||
|
final price = balanceViewModel.fiatConvertationStore.prices[asset];
|
||||||
|
amount = calculateFiatAmountRaw(
|
||||||
|
cryptoAmount: ethereum!.formatterEthereumAmountToDouble(transaction: transaction),
|
||||||
|
price: price);
|
||||||
|
break;
|
||||||
|
case WalletType.nano:
|
||||||
|
amount = calculateFiatAmountRaw(
|
||||||
|
cryptoAmount: nanoUtil!
|
||||||
|
.getRawAsDecimal(nano!.getTransactionAmountRaw(transaction).toString(), nanoUtil!.rawPerNano)
|
||||||
|
.toDouble(),
|
||||||
|
price: price);
|
||||||
break;
|
break;
|
||||||
case WalletType.ethereum:
|
case WalletType.ethereum:
|
||||||
final asset = ethereum!.assetOfTransaction(balanceViewModel.wallet, transaction);
|
final asset = ethereum!.assetOfTransaction(balanceViewModel.wallet, transaction);
|
||||||
|
|
|
@ -86,13 +86,12 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
|
||||||
super(appStore: appStore) {
|
super(appStore: appStore) {
|
||||||
_useTorOnly = _settingsStore.exchangeStatus == ExchangeApiMode.torOnly;
|
_useTorOnly = _settingsStore.exchangeStatus == ExchangeApiMode.torOnly;
|
||||||
_setProviders();
|
_setProviders();
|
||||||
const excludeDepositCurrencies = [CryptoCurrency.btt, CryptoCurrency.nano];
|
const excludeDepositCurrencies = [CryptoCurrency.btt];
|
||||||
const excludeReceiveCurrencies = [
|
const excludeReceiveCurrencies = [
|
||||||
CryptoCurrency.xlm,
|
CryptoCurrency.xlm,
|
||||||
CryptoCurrency.xrp,
|
CryptoCurrency.xrp,
|
||||||
CryptoCurrency.bnb,
|
CryptoCurrency.bnb,
|
||||||
CryptoCurrency.btt,
|
CryptoCurrency.btt
|
||||||
CryptoCurrency.nano
|
|
||||||
];
|
];
|
||||||
_initialPairBasedOnWallet();
|
_initialPairBasedOnWallet();
|
||||||
|
|
||||||
|
@ -703,6 +702,10 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
|
||||||
depositCurrency = CryptoCurrency.eth;
|
depositCurrency = CryptoCurrency.eth;
|
||||||
receiveCurrency = CryptoCurrency.xmr;
|
receiveCurrency = CryptoCurrency.xmr;
|
||||||
break;
|
break;
|
||||||
|
case WalletType.nano:
|
||||||
|
depositCurrency = CryptoCurrency.nano;
|
||||||
|
receiveCurrency = CryptoCurrency.xmr;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
import 'package:cake_wallet/nano/nano.dart';
|
||||||
|
import 'package:cw_core/nano_account.dart';
|
||||||
|
import 'package:cw_core/wallet_base.dart';
|
||||||
|
import 'package:cw_core/wallet_type.dart';
|
||||||
|
|
||||||
|
import 'package:mobx/mobx.dart';
|
||||||
|
import 'package:cake_wallet/core/execution_state.dart';
|
||||||
|
|
||||||
|
part 'nano_account_edit_or_create_view_model.g.dart';
|
||||||
|
|
||||||
|
class NanoAccountEditOrCreateViewModel = NanoAccountEditOrCreateViewModelBase
|
||||||
|
with _$NanoAccountEditOrCreateViewModel;
|
||||||
|
|
||||||
|
abstract class NanoAccountEditOrCreateViewModelBase with Store {
|
||||||
|
NanoAccountEditOrCreateViewModelBase(this._nanoAccountList,
|
||||||
|
{required WalletBase wallet, NanoAccount? accountListItem})
|
||||||
|
: state = InitialExecutionState(),
|
||||||
|
isEdit = accountListItem != null,
|
||||||
|
label = accountListItem?.label ?? '',
|
||||||
|
_accountListItem = accountListItem,
|
||||||
|
_wallet = wallet;
|
||||||
|
|
||||||
|
final bool isEdit;
|
||||||
|
|
||||||
|
@observable
|
||||||
|
ExecutionState state;
|
||||||
|
|
||||||
|
@observable
|
||||||
|
String label;
|
||||||
|
|
||||||
|
final NanoAccountList _nanoAccountList;
|
||||||
|
final NanoAccount? _accountListItem;
|
||||||
|
final WalletBase _wallet;
|
||||||
|
|
||||||
|
Future<void> save() async {
|
||||||
|
if (_wallet.type == WalletType.nano) {
|
||||||
|
await saveNano();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> saveNano() async {
|
||||||
|
try {
|
||||||
|
state = IsExecutingState();
|
||||||
|
|
||||||
|
if (_accountListItem != null) {
|
||||||
|
await _nanoAccountList.setLabelAccount(_wallet,
|
||||||
|
accountIndex: _accountListItem!.id, label: label);
|
||||||
|
} else {
|
||||||
|
await _nanoAccountList.addAccount(_wallet, label: label);
|
||||||
|
}
|
||||||
|
|
||||||
|
await _wallet.save();
|
||||||
|
state = ExecutedSuccessfullyState();
|
||||||
|
} catch (e) {
|
||||||
|
state = FailureState(e.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
import 'package:cake_wallet/nano/nano.dart';
|
||||||
|
import 'package:cw_core/crypto_currency.dart';
|
||||||
|
import 'package:cw_core/wallet_type.dart';
|
||||||
|
import 'package:cw_core/nano_account.dart';
|
||||||
|
import 'package:mobx/mobx.dart';
|
||||||
|
import 'package:cw_core/wallet_base.dart';
|
||||||
|
|
||||||
|
part 'nano_account_list_view_model.g.dart';
|
||||||
|
|
||||||
|
class NanoAccountListViewModel = NanoAccountListViewModelBase with _$NanoAccountListViewModel;
|
||||||
|
|
||||||
|
abstract class NanoAccountListViewModelBase with Store {
|
||||||
|
NanoAccountListViewModelBase(this._wallet) : scrollOffsetFromTop = 0;
|
||||||
|
|
||||||
|
@observable
|
||||||
|
double scrollOffsetFromTop;
|
||||||
|
|
||||||
|
@action
|
||||||
|
void setScrollOffsetFromTop(double scrollOffsetFromTop) {
|
||||||
|
this.scrollOffsetFromTop = scrollOffsetFromTop;
|
||||||
|
}
|
||||||
|
|
||||||
|
CryptoCurrency get currency => _wallet.currency;
|
||||||
|
|
||||||
|
@computed
|
||||||
|
List<NanoAccount> get accounts {
|
||||||
|
if (_wallet.type == WalletType.nano) {
|
||||||
|
return nano!
|
||||||
|
.getAccountList(_wallet)
|
||||||
|
.accounts
|
||||||
|
.map((acc) => NanoAccount(
|
||||||
|
label: acc.label,
|
||||||
|
id: acc.id,
|
||||||
|
isSelected: acc.id == nano?.getCurrentAccount(_wallet).id,
|
||||||
|
))
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
throw Exception('Unexpected wallet type: ${_wallet.type}');
|
||||||
|
}
|
||||||
|
|
||||||
|
final WalletBase _wallet;
|
||||||
|
|
||||||
|
void select(NanoAccount item) {
|
||||||
|
if (_wallet.type == WalletType.nano) {
|
||||||
|
nano!.setCurrentAccount(
|
||||||
|
_wallet,
|
||||||
|
item.id,
|
||||||
|
item.label,
|
||||||
|
item.balance,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -66,6 +66,9 @@ abstract class NodeListViewModelBase with Store {
|
||||||
case WalletType.ethereum:
|
case WalletType.ethereum:
|
||||||
node = getEthereumDefaultNode(nodes: _nodeSource)!;
|
node = getEthereumDefaultNode(nodes: _nodeSource)!;
|
||||||
break;
|
break;
|
||||||
|
case WalletType.nano:
|
||||||
|
node = getNanoDefaultNode(nodes: _nodeSource)!;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
throw Exception('Unexpected wallet type: ${_appStore.wallet!.type}');
|
throw Exception('Unexpected wallet type: ${_appStore.wallet!.type}');
|
||||||
}
|
}
|
||||||
|
|
79
lib/view_model/node_list/pow_node_list_view_model.dart
Normal file
79
lib/view_model/node_list/pow_node_list_view_model.dart
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
import 'package:cake_wallet/generated/i18n.dart';
|
||||||
|
import 'package:cake_wallet/store/app_store.dart';
|
||||||
|
import 'package:cake_wallet/utils/mobx.dart';
|
||||||
|
import 'package:hive/hive.dart';
|
||||||
|
import 'package:mobx/mobx.dart';
|
||||||
|
import 'package:cw_core/wallet_base.dart';
|
||||||
|
import 'package:cake_wallet/store/settings_store.dart';
|
||||||
|
import 'package:cw_core/node.dart';
|
||||||
|
import 'package:cake_wallet/entities/node_list.dart';
|
||||||
|
import 'package:cake_wallet/entities/default_settings_migration.dart';
|
||||||
|
import 'package:cw_core/wallet_type.dart';
|
||||||
|
|
||||||
|
part 'pow_node_list_view_model.g.dart';
|
||||||
|
|
||||||
|
class PowNodeListViewModel = PowNodeListViewModelBase with _$PowNodeListViewModel;
|
||||||
|
|
||||||
|
abstract class PowNodeListViewModelBase with Store {
|
||||||
|
PowNodeListViewModelBase(this._nodeSource, this._appStore)
|
||||||
|
: nodes = ObservableList<Node>(),
|
||||||
|
settingsStore = _appStore.settingsStore {
|
||||||
|
_bindNodes();
|
||||||
|
|
||||||
|
reaction((_) => _appStore.wallet, (WalletBase? _wallet) {
|
||||||
|
_bindNodes();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@computed
|
||||||
|
Node get currentNode {
|
||||||
|
final node = settingsStore.powNodes[_appStore.wallet!.type];
|
||||||
|
|
||||||
|
if (node == null) {
|
||||||
|
throw Exception('No node for wallet type: ${_appStore.wallet!.type}');
|
||||||
|
}
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
String getAlertContent(String uri) =>
|
||||||
|
S.current.change_current_node(uri) +
|
||||||
|
'${uri.endsWith('.onion') || uri.contains('.onion:') ? '\n' + S.current.orbot_running_alert : ''}';
|
||||||
|
|
||||||
|
final ObservableList<Node> nodes;
|
||||||
|
final SettingsStore settingsStore;
|
||||||
|
final Box<Node> _nodeSource;
|
||||||
|
final AppStore _appStore;
|
||||||
|
|
||||||
|
Future<void> reset() async {
|
||||||
|
await resetPowToDefault(_nodeSource);
|
||||||
|
|
||||||
|
Node node;
|
||||||
|
|
||||||
|
switch (_appStore.wallet!.type) {
|
||||||
|
case WalletType.nano:
|
||||||
|
node = getNanoDefaultPowNode(nodes: _nodeSource)!;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw Exception('Unexpected wallet type: ${_appStore.wallet!.type}');
|
||||||
|
}
|
||||||
|
|
||||||
|
await setAsCurrent(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
Future<void> delete(Node node) async => node.delete();
|
||||||
|
|
||||||
|
Future<void> setAsCurrent(Node node) async =>
|
||||||
|
settingsStore.powNodes[_appStore.wallet!.type] = node;
|
||||||
|
|
||||||
|
@action
|
||||||
|
void _bindNodes() {
|
||||||
|
nodes.clear();
|
||||||
|
_nodeSource.bindToList(
|
||||||
|
nodes,
|
||||||
|
filter: (val) => val.type == _appStore.wallet!.type,
|
||||||
|
initialFire: true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,10 +1,13 @@
|
||||||
import 'package:cake_wallet/core/wallet_change_listener_view_model.dart';
|
import 'package:cake_wallet/di.dart';
|
||||||
import 'package:cake_wallet/entities/contact_record.dart';
|
|
||||||
import 'package:cake_wallet/entities/priority_for_wallet_type.dart';
|
import 'package:cake_wallet/entities/priority_for_wallet_type.dart';
|
||||||
import 'package:cake_wallet/entities/transaction_description.dart';
|
import 'package:cake_wallet/entities/transaction_description.dart';
|
||||||
import 'package:cake_wallet/entities/wallet_contact.dart';
|
|
||||||
import 'package:cake_wallet/view_model/contact_list/contact_list_view_model.dart';
|
|
||||||
import 'package:cake_wallet/ethereum/ethereum.dart';
|
import 'package:cake_wallet/ethereum/ethereum.dart';
|
||||||
|
import 'package:cake_wallet/nano/nano.dart';
|
||||||
|
import 'package:cake_wallet/core/wallet_change_listener_view_model.dart';
|
||||||
|
import 'package:cake_wallet/entities/contact_record.dart';
|
||||||
|
import 'package:cake_wallet/entities/wallet_contact.dart';
|
||||||
|
import 'package:cake_wallet/store/app_store.dart';
|
||||||
|
import 'package:cake_wallet/view_model/contact_list/contact_list_view_model.dart';
|
||||||
import 'package:cake_wallet/view_model/dashboard/balance_view_model.dart';
|
import 'package:cake_wallet/view_model/dashboard/balance_view_model.dart';
|
||||||
import 'package:cw_core/transaction_priority.dart';
|
import 'package:cw_core/transaction_priority.dart';
|
||||||
import 'package:cake_wallet/view_model/send/output.dart';
|
import 'package:cake_wallet/view_model/send/output.dart';
|
||||||
|
@ -16,7 +19,6 @@ import 'package:cake_wallet/core/address_validator.dart';
|
||||||
import 'package:cake_wallet/core/amount_validator.dart';
|
import 'package:cake_wallet/core/amount_validator.dart';
|
||||||
import 'package:cw_core/pending_transaction.dart';
|
import 'package:cw_core/pending_transaction.dart';
|
||||||
import 'package:cake_wallet/core/validator.dart';
|
import 'package:cake_wallet/core/validator.dart';
|
||||||
import 'package:cake_wallet/store/app_store.dart';
|
|
||||||
import 'package:cake_wallet/core/execution_state.dart';
|
import 'package:cake_wallet/core/execution_state.dart';
|
||||||
import 'package:cake_wallet/monero/monero.dart';
|
import 'package:cake_wallet/monero/monero.dart';
|
||||||
import 'package:cw_core/sync_status.dart';
|
import 'package:cw_core/sync_status.dart';
|
||||||
|
@ -62,7 +64,7 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||||
final priority = _settingsStore.priority[wallet.type];
|
final priority = _settingsStore.priority[wallet.type];
|
||||||
final priorities = priorityForWalletType(wallet.type);
|
final priorities = priorityForWalletType(wallet.type);
|
||||||
|
|
||||||
if (!priorityForWalletType(wallet.type).contains(priority)) {
|
if (!priorityForWalletType(wallet.type).contains(priority) && priorities.isNotEmpty) {
|
||||||
_settingsStore.priority[wallet.type] = priorities.first;
|
_settingsStore.priority[wallet.type] = priorities.first;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,15 +101,15 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||||
|
|
||||||
@computed
|
@computed
|
||||||
String get pendingTransactionFiatAmount {
|
String get pendingTransactionFiatAmount {
|
||||||
|
if (pendingTransaction == null) {
|
||||||
|
return '0.00';
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (pendingTransaction != null) {
|
final fiat = calculateFiatAmount(
|
||||||
final fiat = calculateFiatAmount(
|
price: _fiatConversationStore.prices[selectedCryptoCurrency]!,
|
||||||
price: _fiatConversationStore.prices[selectedCryptoCurrency]!,
|
cryptoAmount: pendingTransaction!.amountFormatted);
|
||||||
cryptoAmount: pendingTransaction!.amountFormatted);
|
return fiat;
|
||||||
return fiat;
|
|
||||||
} else {
|
|
||||||
return '0.00';
|
|
||||||
}
|
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
return '0.00';
|
return '0.00';
|
||||||
}
|
}
|
||||||
|
@ -191,6 +193,9 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||||
bool get isElectrumWallet =>
|
bool get isElectrumWallet =>
|
||||||
wallet.type == WalletType.bitcoin || wallet.type == WalletType.litecoin;
|
wallet.type == WalletType.bitcoin || wallet.type == WalletType.litecoin;
|
||||||
|
|
||||||
|
@computed
|
||||||
|
bool get hasFees => wallet.type != WalletType.nano && wallet.type != WalletType.banano;
|
||||||
|
|
||||||
@observable
|
@observable
|
||||||
CryptoCurrency selectedCryptoCurrency;
|
CryptoCurrency selectedCryptoCurrency;
|
||||||
|
|
||||||
|
@ -316,6 +321,10 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||||
state = TransactionCommitting();
|
state = TransactionCommitting();
|
||||||
await pendingTransaction!.commit();
|
await pendingTransaction!.commit();
|
||||||
|
|
||||||
|
if (walletType == WalletType.nano) {
|
||||||
|
nano!.updateTransactions(wallet);
|
||||||
|
}
|
||||||
|
|
||||||
if (pendingTransaction!.id.isNotEmpty) {
|
if (pendingTransaction!.id.isNotEmpty) {
|
||||||
_settingsStore.shouldSaveRecipientAddress
|
_settingsStore.shouldSaveRecipientAddress
|
||||||
? await transactionDescriptionBox.add(TransactionDescription(
|
? await transactionDescriptionBox.add(TransactionDescription(
|
||||||
|
@ -380,6 +389,10 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||||
|
|
||||||
return ethereum!.createEthereumTransactionCredentials(outputs,
|
return ethereum!.createEthereumTransactionCredentials(outputs,
|
||||||
priority: priority, currency: selectedCryptoCurrency);
|
priority: priority, currency: selectedCryptoCurrency);
|
||||||
|
case WalletType.nano:
|
||||||
|
return nano!.createNanoTransactionCredentials(
|
||||||
|
outputs,
|
||||||
|
);
|
||||||
default:
|
default:
|
||||||
throw Exception('Unexpected wallet type: ${wallet.type}');
|
throw Exception('Unexpected wallet type: ${wallet.type}');
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,7 @@ abstract class OtherSettingsViewModelBase with Store {
|
||||||
final priority = _settingsStore.priority[_wallet.type];
|
final priority = _settingsStore.priority[_wallet.type];
|
||||||
final priorities = priorityForWalletType(_wallet.type);
|
final priorities = priorityForWalletType(_wallet.type);
|
||||||
|
|
||||||
if (!priorities.contains(priority)) {
|
if (!priorities.contains(priority) && priorities.isNotEmpty) {
|
||||||
_settingsStore.priority[_wallet.type] = priorities.first;
|
_settingsStore.priority[_wallet.type] = priorities.first;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -50,6 +50,14 @@ abstract class OtherSettingsViewModelBase with Store {
|
||||||
}
|
}
|
||||||
|
|
||||||
@computed
|
@computed
|
||||||
|
bool get changeRepresentativeEnabled {
|
||||||
|
if (_wallet.type == WalletType.nano || _wallet.type == WalletType.banano) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
BuyProviderType get buyProviderType { return _settingsStore.defaultBuyProvider; }
|
BuyProviderType get buyProviderType { return _settingsStore.defaultBuyProvider; }
|
||||||
|
|
||||||
String getDisplayPriority(dynamic priority) {
|
String getDisplayPriority(dynamic priority) {
|
||||||
|
|
|
@ -47,6 +47,9 @@ abstract class TransactionDetailsViewModelBase with Store {
|
||||||
case WalletType.ethereum:
|
case WalletType.ethereum:
|
||||||
_addEthereumListItems(tx, dateFormat);
|
_addEthereumListItems(tx, dateFormat);
|
||||||
break;
|
break;
|
||||||
|
case WalletType.nano:
|
||||||
|
_addNanoListItems(tx, dateFormat);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -116,6 +119,10 @@ abstract class TransactionDetailsViewModelBase with Store {
|
||||||
return 'https://explorer.havenprotocol.org/search?value=${txId}';
|
return 'https://explorer.havenprotocol.org/search?value=${txId}';
|
||||||
case WalletType.ethereum:
|
case WalletType.ethereum:
|
||||||
return 'https://etherscan.io/tx/${txId}';
|
return 'https://etherscan.io/tx/${txId}';
|
||||||
|
case WalletType.nano:
|
||||||
|
return 'https://nanolooker.com/block/${txId}';
|
||||||
|
case WalletType.banano:
|
||||||
|
return 'https://bananolooker.com/block/${txId}';
|
||||||
default:
|
default:
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
@ -133,6 +140,10 @@ abstract class TransactionDetailsViewModelBase with Store {
|
||||||
return S.current.view_transaction_on + 'explorer.havenprotocol.org';
|
return S.current.view_transaction_on + 'explorer.havenprotocol.org';
|
||||||
case WalletType.ethereum:
|
case WalletType.ethereum:
|
||||||
return S.current.view_transaction_on + 'etherscan.io';
|
return S.current.view_transaction_on + 'etherscan.io';
|
||||||
|
case WalletType.nano:
|
||||||
|
return S.current.view_transaction_on + 'nanolooker.com';
|
||||||
|
case WalletType.banano:
|
||||||
|
return S.current.view_transaction_on + 'bananolooker.com';
|
||||||
default:
|
default:
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
@ -221,4 +232,18 @@ abstract class TransactionDetailsViewModelBase with Store {
|
||||||
|
|
||||||
items.addAll(_items);
|
items.addAll(_items);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void _addNanoListItems(TransactionInfo tx, DateFormat dateFormat) {
|
||||||
|
final _items = [
|
||||||
|
StandartListItem(title: S.current.transaction_details_transaction_id, value: tx.id),
|
||||||
|
StandartListItem(
|
||||||
|
title: S.current.transaction_details_date, value: dateFormat.format(tx.date)),
|
||||||
|
StandartListItem(title: S.current.confirmations, value: (tx.confirmations > 0).toString()),
|
||||||
|
StandartListItem(title: S.current.transaction_details_height, value: '${tx.height}'),
|
||||||
|
StandartListItem(title: S.current.transaction_details_amount, value: tx.amountFormatted()),
|
||||||
|
];
|
||||||
|
|
||||||
|
items.addAll(_items);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -107,6 +107,22 @@ class EthereumURI extends PaymentURI {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class NanoURI extends PaymentURI {
|
||||||
|
NanoURI({required String amount, required String address})
|
||||||
|
: super(amount: amount, address: address);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
var base = 'nano:' + address;
|
||||||
|
|
||||||
|
if (amount.isNotEmpty) {
|
||||||
|
base += '?amount=${amount.replaceAll(',', '.')}';
|
||||||
|
}
|
||||||
|
|
||||||
|
return base;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewModel with Store {
|
abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewModel with Store {
|
||||||
WalletAddressListViewModelBase({
|
WalletAddressListViewModelBase({
|
||||||
required AppStore appStore,
|
required AppStore appStore,
|
||||||
|
@ -176,6 +192,10 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
|
||||||
return EthereumURI(amount: amount, address: address.address);
|
return EthereumURI(amount: amount, address: address.address);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (wallet.type == WalletType.nano) {
|
||||||
|
return NanoURI(amount: amount, address: address.address);
|
||||||
|
}
|
||||||
|
|
||||||
throw Exception('Unexpected type: ${type.toString()}');
|
throw Exception('Unexpected type: ${type.toString()}');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -252,7 +272,11 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
|
||||||
}
|
}
|
||||||
|
|
||||||
@computed
|
@computed
|
||||||
bool get hasAddressList => wallet.type == WalletType.monero || wallet.type == WalletType.haven;
|
bool get hasAddressList =>
|
||||||
|
wallet.type == WalletType.monero ||
|
||||||
|
wallet.type == WalletType.haven;/* ||
|
||||||
|
wallet.type == WalletType.nano ||
|
||||||
|
wallet.type == WalletType.banano;*/// TODO: nano accounts are disabled for now
|
||||||
|
|
||||||
@computed
|
@computed
|
||||||
bool get showElectrumAddressDisclaimer =>
|
bool get showElectrumAddressDisclaimer =>
|
||||||
|
@ -269,11 +293,16 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
|
||||||
void _init() {
|
void _init() {
|
||||||
_baseItems = [];
|
_baseItems = [];
|
||||||
|
|
||||||
if (wallet.type == WalletType.monero || wallet.type == WalletType.haven) {
|
if (wallet.type == WalletType.monero ||
|
||||||
|
wallet.type == WalletType.haven /*||
|
||||||
|
wallet.type == WalletType.nano ||
|
||||||
|
wallet.type == WalletType.banano*/) {
|
||||||
_baseItems.add(WalletAccountListHeader());
|
_baseItems.add(WalletAccountListHeader());
|
||||||
}
|
}
|
||||||
|
|
||||||
_baseItems.add(WalletAddressListHeader());
|
if (wallet.type != WalletType.nano && wallet.type != WalletType.banano) {
|
||||||
|
_baseItems.add(WalletAddressListHeader());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
|
|
|
@ -35,18 +35,16 @@ abstract class WalletCreationVMBase with Store {
|
||||||
final Box<WalletInfo> _walletInfoSource;
|
final Box<WalletInfo> _walletInfoSource;
|
||||||
final AppStore _appStore;
|
final AppStore _appStore;
|
||||||
|
|
||||||
bool nameExists(String name)
|
bool nameExists(String name) => walletCreationService.exists(name);
|
||||||
=> walletCreationService.exists(name);
|
|
||||||
|
|
||||||
bool typeExists(WalletType type)
|
bool typeExists(WalletType type) => walletCreationService.typeExists(type);
|
||||||
=> walletCreationService.typeExists(type);
|
|
||||||
|
|
||||||
Future<void> create({dynamic options, RestoredWallet? restoreWallet}) async {
|
Future<void> create({dynamic options, RestoredWallet? restoreWallet}) async {
|
||||||
final type = restoreWallet?.type ?? this.type;
|
final type = restoreWallet?.type ?? this.type;
|
||||||
try {
|
try {
|
||||||
state = IsExecutingState();
|
state = IsExecutingState();
|
||||||
if (name.isEmpty) {
|
if (name.isEmpty) {
|
||||||
name = await generateName();
|
name = await generateName();
|
||||||
}
|
}
|
||||||
|
|
||||||
walletCreationService.checkIfExists(name);
|
walletCreationService.checkIfExists(name);
|
||||||
|
@ -55,17 +53,21 @@ abstract class WalletCreationVMBase with Store {
|
||||||
final credentials = restoreWallet != null
|
final credentials = restoreWallet != null
|
||||||
? getCredentialsFromRestoredWallet(options, restoreWallet)
|
? getCredentialsFromRestoredWallet(options, restoreWallet)
|
||||||
: getCredentials(options);
|
: getCredentials(options);
|
||||||
|
|
||||||
final walletInfo = WalletInfo.external(
|
final walletInfo = WalletInfo.external(
|
||||||
id: WalletBase.idFor(name, type),
|
id: WalletBase.idFor(name, type),
|
||||||
name: name,
|
name: name,
|
||||||
type: type,
|
type: type,
|
||||||
isRecovery: isRecovery,
|
isRecovery: isRecovery,
|
||||||
restoreHeight: credentials.height ?? 0,
|
restoreHeight: credentials.height ?? 0,
|
||||||
date: DateTime.now(),
|
date: DateTime.now(),
|
||||||
path: path,
|
path: path,
|
||||||
dirPath: dirPath,
|
dirPath: dirPath,
|
||||||
address: '',
|
address: '',
|
||||||
showIntroCakePayCard: (!walletCreationService.typeExists(type)) && type != WalletType.haven);
|
showIntroCakePayCard: (!walletCreationService.typeExists(type)) && type != WalletType.haven,
|
||||||
|
derivationPath: credentials.derivationPath,
|
||||||
|
derivationType: credentials.derivationType,
|
||||||
|
);
|
||||||
credentials.walletInfo = walletInfo;
|
credentials.walletInfo = walletInfo;
|
||||||
final wallet = restoreWallet != null
|
final wallet = restoreWallet != null
|
||||||
? await processFromRestoredWallet(credentials, restoreWallet)
|
? await processFromRestoredWallet(credentials, restoreWallet)
|
||||||
|
@ -80,15 +82,16 @@ abstract class WalletCreationVMBase with Store {
|
||||||
state = FailureState(e.toString());
|
state = FailureState(e.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
WalletCredentials getCredentials(dynamic options) =>
|
|
||||||
|
WalletCredentials getCredentials(dynamic options) => throw UnimplementedError();
|
||||||
|
|
||||||
|
Future<WalletBase> process(WalletCredentials credentials) => throw UnimplementedError();
|
||||||
|
|
||||||
|
WalletCredentials getCredentialsFromRestoredWallet(
|
||||||
|
dynamic options, RestoredWallet restoreWallet) =>
|
||||||
throw UnimplementedError();
|
throw UnimplementedError();
|
||||||
|
|
||||||
Future<WalletBase> process(WalletCredentials credentials) =>
|
Future<WalletBase> processFromRestoredWallet(
|
||||||
throw UnimplementedError();
|
WalletCredentials credentials, RestoredWallet restoreWallet) =>
|
||||||
|
|
||||||
WalletCredentials getCredentialsFromRestoredWallet(dynamic options, RestoredWallet restoreWallet) =>
|
|
||||||
throw UnimplementedError();
|
|
||||||
|
|
||||||
Future<WalletBase> processFromRestoredWallet(WalletCredentials credentials, RestoredWallet restoreWallet) =>
|
|
||||||
throw UnimplementedError();
|
throw UnimplementedError();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import 'package:cake_wallet/nano/nano.dart';
|
||||||
import 'package:cake_wallet/store/app_store.dart';
|
import 'package:cake_wallet/store/app_store.dart';
|
||||||
import 'package:cw_core/transaction_direction.dart';
|
import 'package:cw_core/transaction_direction.dart';
|
||||||
import 'package:cw_core/transaction_info.dart';
|
import 'package:cw_core/transaction_info.dart';
|
||||||
|
@ -104,6 +105,22 @@ abstract class WalletKeysViewModelBase with Store {
|
||||||
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.nano || _appStore.wallet!.type == WalletType.banano) {
|
||||||
|
|
||||||
|
// we don't necessarily have the seed phrase for nano / banano:
|
||||||
|
if (_appStore.wallet!.seed != null) {
|
||||||
|
items.addAll([
|
||||||
|
StandartListItem(title: S.current.wallet_seed, value: _appStore.wallet!.seed!),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// we always have the hex version of the seed:
|
||||||
|
items.addAll([
|
||||||
|
if (_appStore.wallet!.privateKey != null)
|
||||||
|
StandartListItem(title: S.current.spend_key_private, value: _appStore.wallet!.privateKey!),
|
||||||
|
]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<int?> _currentHeight() async {
|
Future<int?> _currentHeight() async {
|
||||||
|
@ -128,6 +145,10 @@ abstract class WalletKeysViewModelBase with Store {
|
||||||
return 'haven-wallet';
|
return 'haven-wallet';
|
||||||
case WalletType.ethereum:
|
case WalletType.ethereum:
|
||||||
return 'ethereum-wallet';
|
return 'ethereum-wallet';
|
||||||
|
case WalletType.nano:
|
||||||
|
return 'nano-wallet';
|
||||||
|
case WalletType.banano:
|
||||||
|
return 'banano-wallet';
|
||||||
default:
|
default:
|
||||||
throw Exception('Unexpected wallet type: ${_appStore.wallet!.toString()}');
|
throw Exception('Unexpected wallet type: ${_appStore.wallet!.toString()}');
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import 'package:flutter/foundation.dart';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
import 'package:mobx/mobx.dart';
|
import 'package:mobx/mobx.dart';
|
||||||
import 'package:cake_wallet/monero/monero.dart';
|
import 'package:cake_wallet/monero/monero.dart';
|
||||||
|
import 'package:cake_wallet/nano/nano.dart';
|
||||||
import 'package:cake_wallet/store/app_store.dart';
|
import 'package:cake_wallet/store/app_store.dart';
|
||||||
import 'package:cw_core/wallet_base.dart';
|
import 'package:cw_core/wallet_base.dart';
|
||||||
import 'package:cake_wallet/core/wallet_creation_service.dart';
|
import 'package:cake_wallet/core/wallet_creation_service.dart';
|
||||||
|
@ -45,6 +46,8 @@ abstract class WalletNewVMBase extends WalletCreationVM with Store {
|
||||||
name: name, language: options as String);
|
name: name, language: options as String);
|
||||||
case WalletType.ethereum:
|
case WalletType.ethereum:
|
||||||
return ethereum!.createEthereumNewWalletCredentials(name: name);
|
return ethereum!.createEthereumNewWalletCredentials(name: name);
|
||||||
|
case WalletType.nano:
|
||||||
|
return nano!.createNanoNewWalletCredentials(name: name);
|
||||||
default:
|
default:
|
||||||
throw Exception('Unexpected type: ${type.toString()}');;
|
throw Exception('Unexpected type: ${type.toString()}');;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
import 'package:cw_core/wallet_info.dart';
|
||||||
|
import 'package:mobx/mobx.dart';
|
||||||
|
import 'package:cake_wallet/view_model/restore/restore_mode.dart';
|
||||||
|
|
||||||
|
part 'wallet_restore_choose_derivation_view_model.g.dart';
|
||||||
|
|
||||||
|
class WalletRestoreChooseDerivationViewModel = WalletRestoreChooseDerivationViewModelBase
|
||||||
|
with _$WalletRestoreChooseDerivationViewModel;
|
||||||
|
|
||||||
|
abstract class WalletRestoreChooseDerivationViewModelBase with Store {
|
||||||
|
WalletRestoreChooseDerivationViewModelBase({required this.derivationInfos})
|
||||||
|
: mode = WalletRestoreMode.seed {}
|
||||||
|
|
||||||
|
@observable
|
||||||
|
List<DerivationInfo> derivationInfos;
|
||||||
|
|
||||||
|
Future<List<DerivationInfo>> get derivations async {
|
||||||
|
return derivationInfos;
|
||||||
|
}
|
||||||
|
|
||||||
|
@observable
|
||||||
|
WalletRestoreMode mode;
|
||||||
|
}
|
|
@ -1,4 +1,6 @@
|
||||||
import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
||||||
|
import 'package:cake_wallet/di.dart';
|
||||||
|
import 'package:cake_wallet/nano/nano.dart';
|
||||||
import 'package:cake_wallet/ethereum/ethereum.dart';
|
import 'package:cake_wallet/ethereum/ethereum.dart';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
import 'package:mobx/mobx.dart';
|
import 'package:mobx/mobx.dart';
|
||||||
|
@ -16,25 +18,34 @@ import 'package:cake_wallet/view_model/restore/restore_mode.dart';
|
||||||
|
|
||||||
part 'wallet_restore_view_model.g.dart';
|
part 'wallet_restore_view_model.g.dart';
|
||||||
|
|
||||||
class WalletRestoreViewModel = WalletRestoreViewModelBase
|
class WalletRestoreViewModel = WalletRestoreViewModelBase with _$WalletRestoreViewModel;
|
||||||
with _$WalletRestoreViewModel;
|
|
||||||
|
|
||||||
abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
|
abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
|
||||||
WalletRestoreViewModelBase(AppStore appStore, WalletCreationService walletCreationService,
|
WalletRestoreViewModelBase(AppStore appStore, WalletCreationService walletCreationService,
|
||||||
Box<WalletInfo> walletInfoSource,
|
Box<WalletInfo> walletInfoSource,
|
||||||
{required WalletType type})
|
{required WalletType type})
|
||||||
: availableModes =
|
: hasSeedLanguageSelector = type == WalletType.monero || type == WalletType.haven,
|
||||||
(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,
|
hasBlockchainHeightLanguageSelector = type == WalletType.monero || type == WalletType.haven,
|
||||||
hasRestoreFromPrivateKey = type == WalletType.ethereum,
|
hasRestoreFromPrivateKey =
|
||||||
|
type == WalletType.ethereum || type == WalletType.nano || type == WalletType.banano,
|
||||||
isButtonEnabled = false,
|
isButtonEnabled = false,
|
||||||
mode = WalletRestoreMode.seed,
|
mode = WalletRestoreMode.seed,
|
||||||
super(appStore, walletInfoSource, walletCreationService, type: type, isRecovery: true) {
|
super(appStore, walletInfoSource, walletCreationService, type: type, isRecovery: true) {
|
||||||
isButtonEnabled =
|
switch (type) {
|
||||||
!hasSeedLanguageSelector && !hasBlockchainHeightLanguageSelector;
|
case WalletType.monero:
|
||||||
|
case WalletType.haven:
|
||||||
|
case WalletType.ethereum:
|
||||||
|
availableModes = WalletRestoreMode.values;
|
||||||
|
break;
|
||||||
|
case WalletType.nano:
|
||||||
|
case WalletType.banano:
|
||||||
|
availableModes = [WalletRestoreMode.seed, WalletRestoreMode.keys];
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
availableModes = [WalletRestoreMode.seed];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
isButtonEnabled = !hasSeedLanguageSelector && !hasBlockchainHeightLanguageSelector;
|
||||||
walletCreationService.changeWalletType(type: type);
|
walletCreationService.changeWalletType(type: type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,7 +53,7 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
|
||||||
static const electrumSeedMnemonicLength = 24;
|
static const electrumSeedMnemonicLength = 24;
|
||||||
static const electrumShortSeedMnemonicLength = 12;
|
static const electrumShortSeedMnemonicLength = 12;
|
||||||
|
|
||||||
final List<WalletRestoreMode> availableModes;
|
late List<WalletRestoreMode> availableModes;
|
||||||
final bool hasSeedLanguageSelector;
|
final bool hasSeedLanguageSelector;
|
||||||
final bool hasBlockchainHeightLanguageSelector;
|
final bool hasBlockchainHeightLanguageSelector;
|
||||||
final bool hasRestoreFromPrivateKey;
|
final bool hasRestoreFromPrivateKey;
|
||||||
|
@ -58,38 +69,37 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
|
||||||
final password = generateWalletPassword();
|
final password = generateWalletPassword();
|
||||||
final height = options['height'] as int? ?? 0;
|
final height = options['height'] as int? ?? 0;
|
||||||
name = options['name'] as String;
|
name = options['name'] as String;
|
||||||
|
DerivationType? derivationType = options["derivationType"] as DerivationType?;
|
||||||
|
String? derivationPath = options["derivationPath"] as String?;
|
||||||
|
|
||||||
if (mode == WalletRestoreMode.seed) {
|
if (mode == WalletRestoreMode.seed) {
|
||||||
final seed = options['seed'] as String;
|
final seed = options['seed'] as String;
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case WalletType.monero:
|
case WalletType.monero:
|
||||||
return monero!.createMoneroRestoreWalletFromSeedCredentials(
|
return monero!.createMoneroRestoreWalletFromSeedCredentials(
|
||||||
name: name,
|
name: name, height: height, mnemonic: seed, password: password);
|
||||||
height: height,
|
|
||||||
mnemonic: seed,
|
|
||||||
password: password);
|
|
||||||
case WalletType.bitcoin:
|
case WalletType.bitcoin:
|
||||||
return bitcoin!.createBitcoinRestoreWalletFromSeedCredentials(
|
return bitcoin!.createBitcoinRestoreWalletFromSeedCredentials(
|
||||||
name: name,
|
name: name,
|
||||||
mnemonic: seed,
|
mnemonic: seed,
|
||||||
password: password);
|
password: password,
|
||||||
|
);
|
||||||
case WalletType.litecoin:
|
case WalletType.litecoin:
|
||||||
return bitcoin!.createBitcoinRestoreWalletFromSeedCredentials(
|
return bitcoin!.createBitcoinRestoreWalletFromSeedCredentials(
|
||||||
name: name,
|
name: name, mnemonic: seed, password: password);
|
||||||
mnemonic: seed,
|
|
||||||
password: password);
|
|
||||||
case WalletType.haven:
|
case WalletType.haven:
|
||||||
return haven!.createHavenRestoreWalletFromSeedCredentials(
|
return haven!.createHavenRestoreWalletFromSeedCredentials(
|
||||||
name: name,
|
name: name, height: height, mnemonic: seed, password: password);
|
||||||
height: height,
|
|
||||||
mnemonic: seed,
|
|
||||||
password: password);
|
|
||||||
case WalletType.ethereum:
|
case WalletType.ethereum:
|
||||||
return ethereum!.createEthereumRestoreWalletFromSeedCredentials(
|
return ethereum!.createEthereumRestoreWalletFromSeedCredentials(
|
||||||
name: name,
|
name: name, mnemonic: seed, password: password);
|
||||||
mnemonic: seed,
|
case WalletType.nano:
|
||||||
password: password);
|
return nano!.createNanoRestoreWalletFromSeedCredentials(
|
||||||
|
name: name,
|
||||||
|
mnemonic: seed,
|
||||||
|
password: password,
|
||||||
|
derivationType: derivationType,
|
||||||
|
);
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -100,40 +110,73 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
|
||||||
final spendKey = options['spendKey'] as String?;
|
final spendKey = options['spendKey'] as String?;
|
||||||
final address = options['address'] as String?;
|
final address = options['address'] as String?;
|
||||||
|
|
||||||
if (type == WalletType.monero) {
|
switch (type) {
|
||||||
return monero!.createMoneroRestoreWalletFromKeysCredentials(
|
case WalletType.monero:
|
||||||
|
return monero!.createMoneroRestoreWalletFromKeysCredentials(
|
||||||
name: name,
|
name: name,
|
||||||
height: height,
|
height: height,
|
||||||
spendKey: spendKey!,
|
spendKey: spendKey!,
|
||||||
viewKey: viewKey!,
|
viewKey: viewKey!,
|
||||||
address: address!,
|
address: address!,
|
||||||
password: password,
|
password: password,
|
||||||
language: 'English');
|
language: 'English',
|
||||||
}
|
);
|
||||||
|
|
||||||
if (type == WalletType.haven) {
|
case WalletType.haven:
|
||||||
return haven!.createHavenRestoreWalletFromKeysCredentials(
|
return haven!.createHavenRestoreWalletFromKeysCredentials(
|
||||||
name: name,
|
name: name,
|
||||||
height: height,
|
height: height,
|
||||||
spendKey: spendKey!,
|
spendKey: spendKey!,
|
||||||
viewKey: viewKey!,
|
viewKey: viewKey!,
|
||||||
address: address!,
|
address: address!,
|
||||||
password: password,
|
password: password,
|
||||||
language: 'English');
|
language: 'English',
|
||||||
}
|
);
|
||||||
|
|
||||||
if (type == WalletType.ethereum) {
|
case WalletType.ethereum:
|
||||||
return ethereum!.createEthereumRestoreWalletFromPrivateKey(
|
return ethereum!.createEthereumRestoreWalletFromPrivateKey(
|
||||||
name: name,
|
name: name,
|
||||||
privateKey: options['private_key'] as String,
|
privateKey: options['private_key'] as String,
|
||||||
password: password,
|
password: password,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
case WalletType.nano:
|
||||||
|
return nano!.createNanoRestoreWalletFromKeysCredentials(
|
||||||
|
name: name,
|
||||||
|
password: password,
|
||||||
|
seedKey: options['private_key'] as String,
|
||||||
|
derivationType: options["derivationType"] as DerivationType,
|
||||||
|
);
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
throw Exception('Unexpected type: ${type.toString()}');
|
throw Exception('Unexpected type: ${type.toString()}');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<List<DerivationType>> getDerivationTypes(dynamic options) async {
|
||||||
|
final seedKey = options['private_key'] as String?;
|
||||||
|
final mnemonic = options['seed'] as String?;
|
||||||
|
WalletType walletType = options['walletType'] as WalletType;
|
||||||
|
var appStore = getIt.get<AppStore>();
|
||||||
|
var node = appStore.settingsStore.getCurrentNode(walletType);
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case WalletType.nano:
|
||||||
|
return nanoUtil!.compareDerivationMethods(
|
||||||
|
mnemonic: mnemonic,
|
||||||
|
privateKey: seedKey,
|
||||||
|
node: node,
|
||||||
|
);
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// throw Exception('Unexpected type: ${type.toString()}');
|
||||||
|
return [DerivationType.def];
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<WalletBase> process(WalletCredentials credentials) async {
|
Future<WalletBase> process(WalletCredentials credentials) async {
|
||||||
if (mode == WalletRestoreMode.keys) {
|
if (mode == WalletRestoreMode.keys) {
|
||||||
|
|
1
model_generator.sh
Normal file → Executable file
1
model_generator.sh
Normal file → Executable file
|
@ -3,4 +3,5 @@ cd cw_monero && flutter pub get && flutter packages pub run build_runner build -
|
||||||
cd cw_bitcoin && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd ..
|
cd cw_bitcoin && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd ..
|
||||||
cd cw_haven && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd ..
|
cd cw_haven && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd ..
|
||||||
cd cw_ethereum && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd ..
|
cd cw_ethereum && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd ..
|
||||||
|
cd cw_nano && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd ..
|
||||||
flutter packages pub run build_runner build --delete-conflicting-outputs
|
flutter packages pub run build_runner build --delete-conflicting-outputs
|
|
@ -129,6 +129,8 @@ flutter:
|
||||||
- assets/bitcoin_electrum_server_list.yml
|
- assets/bitcoin_electrum_server_list.yml
|
||||||
- assets/litecoin_electrum_server_list.yml
|
- assets/litecoin_electrum_server_list.yml
|
||||||
- assets/ethereum_server_list.yml
|
- assets/ethereum_server_list.yml
|
||||||
|
- assets/nano_node_list.yml
|
||||||
|
- assets/nano_pow_node_list.yml
|
||||||
- assets/text/
|
- assets/text/
|
||||||
- assets/faq/
|
- assets/faq/
|
||||||
- assets/animation/
|
- assets/animation/
|
||||||
|
|
|
@ -673,14 +673,20 @@
|
||||||
"matrix_green_dark_theme": "موضوع ماتريكس الأخضر الداكن",
|
"matrix_green_dark_theme": "موضوع ماتريكس الأخضر الداكن",
|
||||||
"monero_light_theme": " ضوء مونيرو",
|
"monero_light_theme": " ضوء مونيرو",
|
||||||
"etherscan_history": "Etherscan تاريخ",
|
"etherscan_history": "Etherscan تاريخ",
|
||||||
"manage_nodes": "ﺪﻘﻌﻟﺍ ﺓﺭﺍﺩﺇ",
|
|
||||||
"template_name": "اسم القالب",
|
"template_name": "اسم القالب",
|
||||||
|
"change_rep": "ﺏﻭﺪﻨﻣ ﺮﻴﻴﻐﺗ",
|
||||||
|
"change_rep_message": "؟ﻦﻴﻠﺜﻤﻤﻟﺍ ﺮﻴﻴﻐﺗ ﺪﻳﺮﺗ ﻚﻧﺃ ﺪﻛﺄﺘﻣ ﺖﻧﺃ ﻞﻫ",
|
||||||
|
"manage_nodes": "ﺪﻘﻌﻟﺍ ﺓﺭﺍﺩﺇ",
|
||||||
|
"unsupported_asset": ".ﻡﻮﻋﺪﻣ ﻞﺻﺃ ﻉﻮﻧ ﻦﻣ ﺔﻈﻔﺤﻣ ﻰﻟﺇ ﻞﻳﺪﺒﺘﻟﺍ ﻭﺃ ءﺎﺸﻧﺇ ﻰﺟﺮﻳ .ﻞﺻﻷﺍ ﺍﺬﻬﻟ ءﺍﺮﺟﻹﺍ ﺍﺬﻫ ﻢﻋﺪﻧ ﻻ ﻦﺤﻧ",
|
||||||
|
"manage_pow_nodes": "PoW ﻁﺎﻘﻧ ﺓﺭﺍﺩﺇ",
|
||||||
"support_title_live_chat": "الدعم المباشر",
|
"support_title_live_chat": "الدعم المباشر",
|
||||||
"support_description_live_chat": "حرة وسريعة! ممثلو الدعم المدربين متاحون للمساعدة",
|
"support_description_live_chat": "حرة وسريعة! ممثلو الدعم المدربين متاحون للمساعدة",
|
||||||
"support_title_guides": "أدلة محفظة كعكة",
|
"support_title_guides": "أدلة محفظة كعكة",
|
||||||
"support_description_guides": "توثيق ودعم القضايا المشتركة",
|
"support_description_guides": "توثيق ودعم القضايا المشتركة",
|
||||||
"support_title_other_links": "روابط دعم أخرى",
|
"support_title_other_links": "روابط دعم أخرى",
|
||||||
"support_description_other_links": "انضم إلى مجتمعاتنا أو تصل إلينا شركائنا من خلال أساليب أخرى",
|
"support_description_other_links": "انضم إلى مجتمعاتنا أو تصل إلينا شركائنا من خلال أساليب أخرى",
|
||||||
|
"choose_derivation": "اختر اشتقاق المحفظة",
|
||||||
|
"new_first_wallet_text": "حافظ بسهولة على أمان العملة المشفرة",
|
||||||
"select_destination": ".ﻲﻃﺎﻴﺘﺣﻻﺍ ﺦﺴﻨﻟﺍ ﻒﻠﻣ ﺔﻬﺟﻭ ﺪﻳﺪﺤﺗ ءﺎﺟﺮﻟﺍ",
|
"select_destination": ".ﻲﻃﺎﻴﺘﺣﻻﺍ ﺦﺴﻨﻟﺍ ﻒﻠﻣ ﺔﻬﺟﻭ ﺪﻳﺪﺤﺗ ءﺎﺟﺮﻟﺍ",
|
||||||
"auto_generate_subaddresses": "تلقائي توليد subddresses",
|
"auto_generate_subaddresses": "تلقائي توليد subddresses",
|
||||||
"save_to_downloads": "ﺕﻼﻳﺰﻨﺘﻟﺍ ﻲﻓ ﻆﻔﺣ",
|
"save_to_downloads": "ﺕﻼﻳﺰﻨﺘﻟﺍ ﻲﻓ ﻆﻔﺣ",
|
||||||
|
|
|
@ -670,14 +670,20 @@
|
||||||
"matrix_green_dark_theme": "Зелена тъмна тема Matrix",
|
"matrix_green_dark_theme": "Зелена тъмна тема Matrix",
|
||||||
"monero_light_theme": "Лека тема Monero",
|
"monero_light_theme": "Лека тема Monero",
|
||||||
"etherscan_history": "История на Etherscan",
|
"etherscan_history": "История на Etherscan",
|
||||||
"manage_nodes": "Управление на възли",
|
|
||||||
"template_name": "Име на шаблон",
|
"template_name": "Име на шаблон",
|
||||||
|
"change_rep": "Смяна на представител",
|
||||||
|
"change_rep_message": "Сигурни ли сте, че искате да смените представителите?",
|
||||||
|
"manage_nodes": "Управление на възли",
|
||||||
|
"unsupported_asset": "Не поддържаме това действие за този актив. Моля, създайте или преминете към портфейл от поддържан тип актив.",
|
||||||
|
"manage_pow_nodes": "Управление на PoW възли",
|
||||||
"support_title_live_chat": "Подкрепа на живо",
|
"support_title_live_chat": "Подкрепа на живо",
|
||||||
"support_description_live_chat": "Безплатно и бързо! Обучени представители на подкрепата са на разположение за подпомагане",
|
"support_description_live_chat": "Безплатно и бързо! Обучени представители на подкрепата са на разположение за подпомагане",
|
||||||
"support_title_guides": "Ръководства за портфейл за торта",
|
"support_title_guides": "Ръководства за портфейл за торта",
|
||||||
"support_description_guides": "Документация и подкрепа за общи проблеми",
|
"support_description_guides": "Документация и подкрепа за общи проблеми",
|
||||||
"support_title_other_links": "Други връзки за поддръжка",
|
"support_title_other_links": "Други връзки за поддръжка",
|
||||||
"support_description_other_links": "Присъединете се към нашите общности или се свържете с нас нашите партньори чрез други методи",
|
"support_description_other_links": "Присъединете се към нашите общности или се свържете с нас нашите партньори чрез други методи",
|
||||||
|
"choose_derivation": "Изберете производно на портфейла",
|
||||||
|
"new_first_wallet_text": "Лесно пазете криптовалутата си в безопасност",
|
||||||
"select_destination": "Моля, изберете дестинация за архивния файл.",
|
"select_destination": "Моля, изберете дестинация за архивния файл.",
|
||||||
"save_to_downloads": "Запазване в Изтегляния",
|
"save_to_downloads": "Запазване в Изтегляния",
|
||||||
"select_buy_provider_notice": "Изберете доставчик на покупка по -горе. Можете да пропуснете този екран, като зададете вашия доставчик по подразбиране по подразбиране в настройките на приложението.",
|
"select_buy_provider_notice": "Изберете доставчик на покупка по -горе. Можете да пропуснете този екран, като зададете вашия доставчик по подразбиране по подразбиране в настройките на приложението.",
|
||||||
|
|
|
@ -672,12 +672,18 @@
|
||||||
"manage_nodes": "Spravovat uzly",
|
"manage_nodes": "Spravovat uzly",
|
||||||
"etherscan_history": "Historie Etherscanu",
|
"etherscan_history": "Historie Etherscanu",
|
||||||
"template_name": "Název šablony",
|
"template_name": "Název šablony",
|
||||||
|
"change_rep": "Změna zástupce",
|
||||||
|
"change_rep_message": "Jste si jisti, že chcete změnit zástupce?",
|
||||||
|
"unsupported_asset": "Tuto akci u tohoto díla nepodporujeme. Vytvořte nebo přepněte na peněženku podporovaného typu aktiv.",
|
||||||
|
"manage_pow_nodes": "Správa uzlů PoW",
|
||||||
"support_title_live_chat": "Živá podpora",
|
"support_title_live_chat": "Živá podpora",
|
||||||
"support_description_live_chat": "Zdarma a rychle! K dispozici jsou zástupci vyškolených podpůrných podpory",
|
"support_description_live_chat": "Zdarma a rychle! K dispozici jsou zástupci vyškolených podpůrných podpory",
|
||||||
"support_title_guides": "Průvodce peněženkami dortu",
|
"support_title_guides": "Průvodce peněženkami dortu",
|
||||||
"support_description_guides": "Dokumentace a podpora běžných otázek",
|
"support_description_guides": "Dokumentace a podpora běžných otázek",
|
||||||
"support_title_other_links": "Další odkazy na podporu",
|
"support_title_other_links": "Další odkazy na podporu",
|
||||||
"support_description_other_links": "Připojte se k našim komunitám nebo se k nám oslovte další metody",
|
"support_description_other_links": "Připojte se k našim komunitám nebo se k nám oslovte další metody",
|
||||||
|
"choose_derivation": "Vyberte derivaci peněženky",
|
||||||
|
"new_first_wallet_text": "Snadno udržujte svou kryptoměnu v bezpečí",
|
||||||
"select_destination": "Vyberte cíl pro záložní soubor.",
|
"select_destination": "Vyberte cíl pro záložní soubor.",
|
||||||
"save_to_downloads": "Uložit do Stažených souborů",
|
"save_to_downloads": "Uložit do Stažených souborů",
|
||||||
"select_buy_provider_notice": "Vyberte výše uvedeného poskytovatele nákupu. Tuto obrazovku můžete přeskočit nastavením výchozího poskytovatele nákupu v nastavení aplikace.",
|
"select_buy_provider_notice": "Vyberte výše uvedeného poskytovatele nákupu. Tuto obrazovku můžete přeskočit nastavením výchozího poskytovatele nákupu v nastavení aplikace.",
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue