mirror of
https://github.com/cake-tech/cake_wallet.git
synced 2025-03-07 09:15:07 +00:00
Zano with passphrase (#1971)
* CW-685 Add passphrase restore for xmr/wow (#1552) * CW-685 Add passphrase restore for xmr/wow * add support for polyseed passphrase * disable 14 word seed passphrase (not supported in wownero-seed) fix: Getting grayed screen on latest passphrase build after having restored a 14-word wownero seed (+passphrase) and attempting to restore a XMR seed, legacy or otherwise. * fix pointer when restoring depracated wownero seed * Fix polyseed encryption * changes from review * remove unused code * add passphrase back to the screen add passphrase to qr code backup export * fix settings leaking through currencies on seed restore * fix monero.com builds, make passphrase a getter on WalletBase * add support for weird polyseed * store passphrase for weird polyseed * show encrypted seed only when passphrase is not empty * force set restore height * fix build issues * fix build errors * fix configure script * print -> printV * Update lib/view_model/wallet_keys_view_model.dart [skip ci] * Update lib/view_model/wallet_keys_view_model.dart [skip ci] * Update tool/configure.dart [skip ci] * Update lib/view_model/wallet_new_vm.dart * reuse existing passphrase field * remove unused passphrase field * make workflow run on pullrequests only [skip ci] [skip slack] --------- Co-authored-by: Konstantin Ullrich <konstantinullrich12@gmail.com> Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> * Zano (#1793) * my experiments * Inital code for Zano integration * Added missing android log lib * added dummy wallet & some zano implementation * fixing api for zano * fixed zano build script * attempt tp fix namespace problem * added copy script for Zano files * changes for zano wallet * last updates * zano.dart test app * wallet recovery * added pending transfer, some cleanup of unused * some cleanup * send + receive qr code * last upd * updated build_zano.sh * updated zano ApiCalls, removed dummy * updated zano ApiCalls, removed dummy * added logging for get status/get info * restored old wallet.dart * restoring original versions of files * restoring original versions of files * restored get_height_by_date.dart, removed unnecessary calls for zano get height * restoring original versions * added multiple destinations, send all flag; some refactoring * logging * removed the duplicate * fixed syncing sync status, decimal division, safe null json parsing * some fixes after merge * added multibalance/asset support for zano (ui) * adding/removing from whitelist * transfers in different assets * transfers for multiple destinations and send all, some refactoring * whitelists, some refactoring * added different digits (decimal points) for formatters, some refactoring * open, create, restore wallet refactoring; whitelists * whitelists * getting and updating transaction list; restoring a wallet from QR code * several attempts to close wallet * some refactoring * added seed phrase * changed fields to BigInt, some fixes * modified build scripts for android * build scripts * restored accidently removed cw_haven.dart * inital ios integration(zano libs built) * update in script * latest changes * Applied a patch for iOS build (Boost and Zano scripts) * Removed zano.dart (script-generated) and some unnecessary files * Revert "Removed zano.dart (script-generated) and some unnecessary files" This reverts commit367c86398e
. * Removed zano.dart (generated by scripts), some files restored to initial versions * added timer library * changed paths in build_zano.sh * build_zano.sh * edit_token_page.dart - removed flag skipZanoAddressValidation Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> * lib/core/address_validator.dart - updated zano address regex lib/src/screens/dashboard/edit_token_page.dart - using AddressValidator().call * fix zano build issues on android * remove contrib/depends to save space * move async call to a synchronouse one * call sync call in isolate to make it async generate framework for iOS as well fix UR issues * zano changes from monero_c repo * update monero_c hash * fix invalid zano imports, add support for linux, speed up CI builds * update monero_c hash * bump monero_c commit (yes, again, I know) * fix wallet resttore, fix hardcoded IP * fix regex, don't throw error when opening wallet, fix tx history, fix async calls, move stuff to isolate * fix api calls in async transaction creation * update build scripts * fix some build issues * update dependencies * fix dependencies * update ci scripts * Improve multithread use of zano api * Fix build issue * fix zano node selection, move other zano calls to separate isolate * update moneroc hash WIP fixes for zano * update monero_c * fix monero.com builds * sync wallet after connecting * update monero_c * Fix windows builds * update monero_c * update monero_c * unshallow submodule * cherry pick CW-867 Wownero fixes (#1881) * fix wownero syntax error * remove print statements in zano * update zano node URL * [PATCH] Apply new CI script (https://github.com/cake-tech/cake_wallet/pull/1948) * drop env -i to fix cmake build errors on newer system * [skip ci] Revert "[PATCH] Apply new CI script (https://github.com/cake-tech/cake_wallet/pull/1948)" This reverts commit5acb5bfe57
. * [run tests] [skip slack] Fix env in build * Dynamically detect number of cores used to build monero_c, since it appears that zano requires more memory to link (and it reliably fails for first couple builds due to OOM on CI/VM with memory constrains). Drop unshallowing of all modules [run tests] * Changes from review [run tests] * drop zano on linux (missing symbols) fix wownero on linux add aarch64-linux-gnu [run tests] * - remove duplicate entry in addToken() - use walletPassword in createZanoNewWalletCredentials - remove createZanoRestoreWalletFromKeysCredentials [run tests] * [skip ci] update dockerfile * fix parameter issue --------- Co-authored-by: leo <leonid.ivanov@gmail.com> Co-authored-by: cr.zoidberg <crypto.zoidberg@gmail.com> Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> * - Add Zano Aliases - Enable simpleswap [skip ci] - Fix settings migration versions * push missing file [skip ci] * Zano pr with CW-685 passphrase (#1969) * my experiments * Inital code for Zano integration * Added missing android log lib * added dummy wallet & some zano implementation * fixing api for zano * fixed zano build script * attempt tp fix namespace problem * added copy script for Zano files * changes for zano wallet * last updates * zano.dart test app * wallet recovery * added pending transfer, some cleanup of unused * some cleanup * send + receive qr code * last upd * updated build_zano.sh * updated zano ApiCalls, removed dummy * updated zano ApiCalls, removed dummy * added logging for get status/get info * restored old wallet.dart * restoring original versions of files * restoring original versions of files * restored get_height_by_date.dart, removed unnecessary calls for zano get height * restoring original versions * added multiple destinations, send all flag; some refactoring * logging * removed the duplicate * fixed syncing sync status, decimal division, safe null json parsing * some fixes after merge * added multibalance/asset support for zano (ui) * adding/removing from whitelist * transfers in different assets * transfers for multiple destinations and send all, some refactoring * whitelists, some refactoring * added different digits (decimal points) for formatters, some refactoring * open, create, restore wallet refactoring; whitelists * whitelists * getting and updating transaction list; restoring a wallet from QR code * several attempts to close wallet * some refactoring * added seed phrase * CW-685 Add passphrase restore for xmr/wow * add support for polyseed passphrase * disable 14 word seed passphrase (not supported in wownero-seed) fix: Getting grayed screen on latest passphrase build after having restored a 14-word wownero seed (+passphrase) and attempting to restore a XMR seed, legacy or otherwise. * fix pointer when restoring depracated wownero seed * Fix polyseed encryption * changed fields to BigInt, some fixes * modified build scripts for android * build scripts * restored accidently removed cw_haven.dart * inital ios integration(zano libs built) * update in script * latest changes * changes from review * remove unused code * add passphrase back to the screen add passphrase to qr code backup export * fix settings leaking through currencies on seed restore * fix monero.com builds, make passphrase a getter on WalletBase * add support for weird polyseed * store passphrase for weird polyseed * show encrypted seed only when passphrase is not empty * force set restore height * Applied a patch for iOS build (Boost and Zano scripts) * Removed zano.dart (script-generated) and some unnecessary files * Revert "Removed zano.dart (script-generated) and some unnecessary files" This reverts commit367c86398e
. * Removed zano.dart (generated by scripts), some files restored to initial versions * fix build issues * fix build errors * added timer library * changed paths in build_zano.sh * build_zano.sh * edit_token_page.dart - removed flag skipZanoAddressValidation Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> * lib/core/address_validator.dart - updated zano address regex lib/src/screens/dashboard/edit_token_page.dart - using AddressValidator().call * fix zano build issues on android * remove contrib/depends to save space * move async call to a synchronouse one * call sync call in isolate to make it async generate framework for iOS as well fix UR issues * zano changes from monero_c repo * update monero_c hash * fix invalid zano imports, add support for linux, speed up CI builds * update monero_c hash * bump monero_c commit (yes, again, I know) * fix wallet resttore, fix hardcoded IP * fix regex, don't throw error when opening wallet, fix tx history, fix async calls, move stuff to isolate * fix api calls in async transaction creation * fix configure script * update build scripts * fix some build issues * update dependencies * fix dependencies * update ci scripts * Improve multithread use of zano api * Fix build issue * fix zano node selection, move other zano calls to separate isolate * update moneroc hash WIP fixes for zano * update monero_c * fix monero.com builds * sync wallet after connecting * update monero_c * Fix windows builds * update monero_c * print -> printV * update monero_c * unshallow submodule * cherry pick CW-867 Wownero fixes (#1881) * fix wownero syntax error * remove print statements in zano * update zano node URL * [PATCH] Apply new CI script (https://github.com/cake-tech/cake_wallet/pull/1948) * drop env -i to fix cmake build errors on newer system * Update lib/view_model/wallet_keys_view_model.dart [skip ci] * Update lib/view_model/wallet_keys_view_model.dart [skip ci] * Update tool/configure.dart [skip ci] * Update lib/view_model/wallet_new_vm.dart * [skip ci] Revert "[PATCH] Apply new CI script (https://github.com/cake-tech/cake_wallet/pull/1948)" This reverts commit5acb5bfe57
. * [run tests] [skip slack] Fix env in build * Dynamically detect number of cores used to build monero_c, since it appears that zano requires more memory to link (and it reliably fails for first couple builds due to OOM on CI/VM with memory constrains). Drop unshallowing of all modules [run tests] * Changes from review [run tests] * drop zano on linux (missing symbols) fix wownero on linux add aarch64-linux-gnu [run tests] * - remove duplicate entry in addToken() - use walletPassword in createZanoNewWalletCredentials - remove createZanoRestoreWalletFromKeysCredentials [run tests] * [skip ci] update dockerfile * reuse existing passphrase field * add passphrase support for zano * Drop aarch64-linux-gnu for now. * fix passphrase display, fix gray screen * catch errors in polyseed encryption, encrypt only polyseed, fix coin in wownero * update monero_c update wownero to 0.11.3.0 * Show passphrase only when non-empty, fix passphrase being displayed as view key private. * fix NanoAccountListPage showing up instead of MoneroAccountListPage for wownero * build zano dependencies on android * fix parameter issue * minor merge leftover [skip ci] * minor cleanup [skip ci] * fix zano alias update eth url for ens lookup change $MAKE_JOB_COUNT to $NPROC * minor cleanup [skip ci] * fix zano alias * Disable passphrase for creation of xmr/wow/zano minor fixes * fix zano on iOS * - Fix get token data - Enable unavailable balance - Enable confirmations count - Adjust explorer link --------- Co-authored-by: leo <leonid.ivanov@gmail.com> Co-authored-by: Konstantin Ullrich <konstantinullrich12@gmail.com> Co-authored-by: cr.zoidberg <crypto.zoidberg@gmail.com> Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> --------- Co-authored-by: cyan <cyjan@mrcyjanek.net> Co-authored-by: Konstantin Ullrich <konstantinullrich12@gmail.com> Co-authored-by: leo <leonid.ivanov@gmail.com> Co-authored-by: cr.zoidberg <crypto.zoidberg@gmail.com>
This commit is contained in:
parent
9cda2c99e7
commit
df3a26dc15
169 changed files with 6113 additions and 212 deletions
.github/workflows
.gitignoreanalysis_options.yamlandroid/app/src/main
assets
cw_bitcoin
cw_core
lib
amount_converter.dartcrypto_currency.dartcurrency_for_wallet_type.darthive_type_ids.dartmonero_wallet_keys.dartnode.dartwallet_type.dartzano_asset.dart
pubspec.lockpubspec.yamlcw_haven
cw_monero
cw_wownero
cw_zano
.gitignore.metadataCHANGELOG.mdLICENSEREADME.mdpubspec.lockpubspec.yaml
lib
api
consts.dart
model
asset_id_params.dartbalance.dartcreate_wallet_result.dartdestination.dartemployed_entries.dartget_address_info_result.dartget_recent_txs_and_info_params.dartget_recent_txs_and_info_result.dartget_wallet_info_result.dartget_wallet_status_result.dartproxy_to_daemon_params.dartproxy_to_daemon_result.dartreceive.dartrecent_history.dartstore_result.dartsubtransfer.darttransfer.darttransfer_params.darttransfer_result.dartwi.dartwi_extended.dart
mnemonics
model
pending_zano_transaction.dartzano_asset.dartzano_balance.dartzano_transaction_creation_exception.dartzano_transaction_credentials.dartzano_transaction_info.dartzano_wallet_keys.dart
zano_formatter.dartzano_transaction_history.dartzano_utils.dartzano_wallet.dartzano_wallet_addresses.dartzano_wallet_api.dartzano_wallet_exceptions.dartzano_wallet_service.dartios
.gitignore
Runner.xcodeproj
ZanoWallet.framework
monero_libwallet2_api_c.dylibwownero_libwallet2_api_c.dylibzano_libwallet2_api_c.dyliblib
2
.github/workflows/pr_test_build_android.yml
vendored
2
.github/workflows/pr_test_build_android.yml
vendored
|
@ -1,6 +1,6 @@
|
|||
name: Cake Wallet Android
|
||||
|
||||
on: [push]
|
||||
on: [pull_request]
|
||||
|
||||
defaults:
|
||||
run:
|
||||
|
|
2
.github/workflows/pr_test_build_linux.yml
vendored
2
.github/workflows/pr_test_build_linux.yml
vendored
|
@ -1,6 +1,6 @@
|
|||
name: Cake Wallet Linux
|
||||
|
||||
on: [push]
|
||||
on: [pull_request]
|
||||
|
||||
defaults:
|
||||
run:
|
||||
|
|
5
.gitignore
vendored
5
.gitignore
vendored
|
@ -126,7 +126,7 @@ cw_shared_external/ios/External/
|
|||
cw_haven/ios/External/
|
||||
cw_haven/android/.externalNativeBuild/
|
||||
cw_haven/android/.cxx/
|
||||
|
||||
cw_zano/ios/External/
|
||||
lib/bitcoin/bitcoin.dart
|
||||
lib/monero/monero.dart
|
||||
lib/haven/haven.dart
|
||||
|
@ -137,6 +137,7 @@ lib/polygon/polygon.dart
|
|||
lib/solana/solana.dart
|
||||
lib/tron/tron.dart
|
||||
lib/wownero/wownero.dart
|
||||
lib/zano/zano.dart
|
||||
|
||||
ios/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_180.png
|
||||
ios/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_120.png
|
||||
|
@ -179,3 +180,5 @@ scripts/monero_c
|
|||
# iOS generated framework bin
|
||||
ios/MoneroWallet.framework/MoneroWallet
|
||||
ios/WowneroWallet.framework/WowneroWallet
|
||||
ios/ZanoWallet.framework/ZanoWallet
|
||||
*_libwallet2_api_c.dylib
|
||||
|
|
|
@ -22,6 +22,7 @@ analyzer:
|
|||
lib/solana/cw_solana.dart,
|
||||
lib/tron/cw_tron.dart,
|
||||
lib/wownero/cw_wownero.dart,
|
||||
lib/zano/cw_zano.dart,
|
||||
]
|
||||
language:
|
||||
strict-casts: true
|
||||
|
|
|
@ -89,6 +89,9 @@
|
|||
<data android:scheme="wownero" />
|
||||
<data android:scheme="wownero-wallet" />
|
||||
<data android:scheme="wownero_wallet" />
|
||||
<data android:scheme="zano" />
|
||||
<data android:scheme="zano-wallet" />
|
||||
<data android:scheme="zano_wallet" />
|
||||
</intent-filter>
|
||||
<!-- nano-gpt link scheme -->
|
||||
<intent-filter android:autoVerify="true">
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
../../../../../../scripts/monero_c/release/zano/aarch64-linux-android_libwallet2_api_c.so
|
|
@ -0,0 +1 @@
|
|||
../../../../../../scripts/monero_c/release/zano/armv7a-linux-androideabi_libwallet2_api_c.so
|
1
android/app/src/main/jniLibs/x86_64/libzano_libwallet2_api_c.so
Symbolic link
1
android/app/src/main/jniLibs/x86_64/libzano_libwallet2_api_c.so
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../../../../../scripts/monero_c/release/zano/x86_64-linux-android_libwallet2_api_c.so
|
BIN
assets/images/zano_icon.png
Normal file
BIN
assets/images/zano_icon.png
Normal file
Binary file not shown.
After ![]() (image error) Size: 1.8 KiB |
4
assets/zano_node_list.yml
Normal file
4
assets/zano_node_list.yml
Normal file
|
@ -0,0 +1,4 @@
|
|||
-
|
||||
uri: 37.27.100.59:10500
|
||||
is_default: true
|
||||
useSSL: false
|
|
@ -316,6 +316,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.10"
|
||||
decimal:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: decimal
|
||||
sha256: "24a261d5d5c87e86c7651c417a5dbdf8bcd7080dd592533910e8d0505a279f21"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.3"
|
||||
encrypt:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -797,6 +805,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.2.2"
|
||||
rational:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: rational
|
||||
sha256: cb808fb6f1a839e6fc5f7d8cb3b0a10e1db48b3be102de73938c627f0b636336
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.3"
|
||||
rxdart:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import 'package:decimal/decimal.dart';
|
||||
import 'package:decimal/intl.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
|
||||
|
@ -43,6 +45,8 @@ class AmountConverter {
|
|||
case CryptoCurrency.xnzd:
|
||||
case CryptoCurrency.xusd:
|
||||
return _moneroAmountToString(amount);
|
||||
case CryptoCurrency.zano:
|
||||
return _moneroAmountToStringUsingDecimals(amount);
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
|
@ -59,4 +63,10 @@ class AmountConverter {
|
|||
|
||||
static String _wowneroAmountToString(int amount) => _wowneroAmountFormat
|
||||
.format(cryptoAmountToDouble(amount: amount, divider: _wowneroAmountDivider));
|
||||
|
||||
static Decimal cryptoAmountToDecimal({required int amount, required int divider}) =>
|
||||
(Decimal.fromInt(amount) / Decimal.fromInt(divider)).toDecimal();
|
||||
|
||||
static String _moneroAmountToStringUsingDecimals(int amount) => _moneroAmountFormat.format(
|
||||
DecimalIntl(cryptoAmountToDecimal(amount: amount, divider: _moneroAmountDivider)));
|
||||
}
|
||||
|
|
|
@ -106,6 +106,7 @@ class CryptoCurrency extends EnumerableItem<int> with Serializable<int> implemen
|
|||
CryptoCurrency.usdcTrc20,
|
||||
CryptoCurrency.tbtc,
|
||||
CryptoCurrency.wow,
|
||||
CryptoCurrency.zano,
|
||||
CryptoCurrency.ton,
|
||||
CryptoCurrency.flip
|
||||
];
|
||||
|
@ -226,8 +227,8 @@ class CryptoCurrency extends EnumerableItem<int> with Serializable<int> implemen
|
|||
static const tbtc = CryptoCurrency(title: 'tBTC', fullName: 'Testnet Bitcoin', raw: 93, name: 'tbtc', iconPath: 'assets/images/tbtc.png', decimals: 8);
|
||||
static const wow = CryptoCurrency(title: 'WOW', fullName: 'Wownero', raw: 94, name: 'wow', iconPath: 'assets/images/wownero_icon.png', decimals: 11);
|
||||
static const ton = CryptoCurrency(title: 'TON', fullName: 'Toncoin', raw: 95, name: 'ton', iconPath: 'assets/images/ton_icon.png', decimals: 8);
|
||||
|
||||
static const flip = CryptoCurrency(title: 'FLIP', tag: 'ETH', fullName: 'Chainflip', raw: 96, name: 'flip', iconPath: 'assets/images/flip_icon.png', decimals: 18);
|
||||
static const zano = CryptoCurrency(title: 'ZANO', tag: 'ZANO', fullName: 'Zano', raw: 96, name: 'zano', iconPath: 'assets/images/zano_icon.png', decimals: 12);
|
||||
static const flip = CryptoCurrency(title: 'FLIP', tag: 'ETH', fullName: 'Chainflip', raw: 97, name: 'flip', iconPath: 'assets/images/flip_icon.png', decimals: 18);
|
||||
|
||||
static final Map<int, CryptoCurrency> _rawCurrencyMap =
|
||||
[...all, ...havenCurrencies].fold<Map<int, CryptoCurrency>>(<int, CryptoCurrency>{}, (acc, item) {
|
||||
|
|
|
@ -30,8 +30,11 @@ CryptoCurrency currencyForWalletType(WalletType type, {bool? isTestnet}) {
|
|||
return CryptoCurrency.trx;
|
||||
case WalletType.wownero:
|
||||
return CryptoCurrency.wow;
|
||||
case WalletType.zano:
|
||||
return CryptoCurrency.zano;
|
||||
case WalletType.none:
|
||||
throw Exception(
|
||||
|
||||
'Unexpected wallet type: ${type.toString()} for CryptoCurrency currencyForWalletType');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,4 +19,5 @@ const DERIVATION_INFO_TYPE_ID = 17;
|
|||
const TRON_TOKEN_TYPE_ID = 18;
|
||||
const HARDWARE_WALLET_TYPE_TYPE_ID = 19;
|
||||
const MWEB_UTXO_TYPE_ID = 20;
|
||||
const HAVEN_SEED_STORE_TYPE_ID = 21;
|
||||
const HAVEN_SEED_STORE_TYPE_ID = 21;
|
||||
const ZANO_ASSET_TYPE_ID = 22;
|
||||
|
|
|
@ -4,11 +4,13 @@ class MoneroWalletKeys {
|
|||
required this.privateSpendKey,
|
||||
required this.privateViewKey,
|
||||
required this.publicSpendKey,
|
||||
required this.publicViewKey});
|
||||
required this.publicViewKey,
|
||||
required this.passphrase});
|
||||
|
||||
final String primaryAddress;
|
||||
final String publicViewKey;
|
||||
final String privateViewKey;
|
||||
final String publicSpendKey;
|
||||
final String privateSpendKey;
|
||||
final String passphrase;
|
||||
}
|
|
@ -1,5 +1,4 @@
|
|||
import 'dart:io';
|
||||
import 'dart:math';
|
||||
import 'package:cw_core/keyable.dart';
|
||||
import 'package:cw_core/utils/print_verbose.dart';
|
||||
import 'dart:convert';
|
||||
|
@ -10,7 +9,6 @@ import 'package:cw_core/wallet_type.dart';
|
|||
import 'package:http/io_client.dart' as ioc;
|
||||
import 'dart:math' as math;
|
||||
import 'package:convert/convert.dart';
|
||||
import 'package:crypto/crypto.dart' as crypto;
|
||||
|
||||
import 'package:crypto/crypto.dart';
|
||||
|
||||
|
@ -105,7 +103,9 @@ class Node extends HiveObject with Keyable {
|
|||
case WalletType.solana:
|
||||
case WalletType.tron:
|
||||
return Uri.parse(
|
||||
"http${isSSL ? "s" : ""}://$uriRaw${path!.startsWith("/") ? path : "/$path"}");
|
||||
"http${isSSL ? "s" : ""}://$uriRaw${path!.startsWith("/") ? path : "/$path"}");
|
||||
case WalletType.zano:
|
||||
return Uri.https(uriRaw, '');
|
||||
case WalletType.none:
|
||||
throw Exception('Unexpected type ${type.toString()} for Node uri');
|
||||
}
|
||||
|
@ -166,6 +166,8 @@ class Node extends HiveObject with Keyable {
|
|||
case WalletType.solana:
|
||||
case WalletType.tron:
|
||||
return requestElectrumServer();
|
||||
case WalletType.zano:
|
||||
return requestZanoNode();
|
||||
case WalletType.none:
|
||||
return false;
|
||||
}
|
||||
|
@ -174,7 +176,11 @@ class Node extends HiveObject with Keyable {
|
|||
}
|
||||
}
|
||||
|
||||
Future<bool> requestMoneroNode() async {
|
||||
Future<bool> requestZanoNode() async {
|
||||
return requestMoneroNode(methodName: "getinfo");
|
||||
}
|
||||
|
||||
Future<bool> requestMoneroNode({String methodName = 'get_info'}) async {
|
||||
if (useSocksProxy) {
|
||||
return await requestNodeWithProxy();
|
||||
}
|
||||
|
@ -182,8 +188,7 @@ class Node extends HiveObject with Keyable {
|
|||
|
||||
final path = '/json_rpc';
|
||||
final rpcUri = isSSL ? Uri.https(uri.authority, path) : Uri.http(uri.authority, path);
|
||||
final body = {'jsonrpc': '2.0', 'id': '0', 'method': 'get_info'};
|
||||
|
||||
final body = {'jsonrpc': '2.0', 'id': '0', 'method': methodName};
|
||||
|
||||
try {
|
||||
final authenticatingClient = HttpClient();
|
||||
|
@ -226,7 +231,7 @@ class Node extends HiveObject with Keyable {
|
|||
final oldUseSSL = useSSL;
|
||||
useSSL = true;
|
||||
try {
|
||||
final ret = await requestMoneroNode();
|
||||
final ret = await requestMoneroNode(methodName: methodName);
|
||||
if (ret == true) {
|
||||
await save();
|
||||
return ret;
|
||||
|
|
|
@ -16,6 +16,7 @@ const walletTypes = [
|
|||
WalletType.polygon,
|
||||
WalletType.solana,
|
||||
WalletType.tron,
|
||||
WalletType.zano,
|
||||
];
|
||||
|
||||
@HiveType(typeId: WALLET_TYPE_TYPE_ID)
|
||||
|
@ -58,6 +59,10 @@ enum WalletType {
|
|||
|
||||
@HiveField(12)
|
||||
wownero,
|
||||
|
||||
@HiveField(13)
|
||||
zano,
|
||||
|
||||
}
|
||||
|
||||
int serializeToInt(WalletType type) {
|
||||
|
@ -86,6 +91,8 @@ int serializeToInt(WalletType type) {
|
|||
return 10;
|
||||
case WalletType.wownero:
|
||||
return 11;
|
||||
case WalletType.zano:
|
||||
return 12;
|
||||
case WalletType.none:
|
||||
return -1;
|
||||
}
|
||||
|
@ -117,8 +124,11 @@ WalletType deserializeFromInt(int raw) {
|
|||
return WalletType.tron;
|
||||
case 11:
|
||||
return WalletType.wownero;
|
||||
case 12:
|
||||
return WalletType.zano;
|
||||
default:
|
||||
throw Exception('Unexpected token: $raw for WalletType deserializeFromInt');
|
||||
throw Exception(
|
||||
'Unexpected token: $raw for WalletType deserializeFromInt');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -148,6 +158,8 @@ String walletTypeToString(WalletType type) {
|
|||
return 'Tron';
|
||||
case WalletType.wownero:
|
||||
return 'Wownero';
|
||||
case WalletType.zano:
|
||||
return 'Zano';
|
||||
case WalletType.none:
|
||||
return '';
|
||||
}
|
||||
|
@ -179,6 +191,8 @@ String walletTypeToDisplayName(WalletType type) {
|
|||
return 'Tron (TRX)';
|
||||
case WalletType.wownero:
|
||||
return 'Wownero (WOW)';
|
||||
case WalletType.zano:
|
||||
return 'Zano (ZANO)';
|
||||
case WalletType.none:
|
||||
return '';
|
||||
}
|
||||
|
@ -213,6 +227,8 @@ CryptoCurrency walletTypeToCryptoCurrency(WalletType type, {bool isTestnet = fal
|
|||
return CryptoCurrency.trx;
|
||||
case WalletType.wownero:
|
||||
return CryptoCurrency.wow;
|
||||
case WalletType.zano:
|
||||
return CryptoCurrency.zano;
|
||||
case WalletType.none:
|
||||
throw Exception(
|
||||
'Unexpected wallet type: ${type.toString()} for CryptoCurrency walletTypeToCryptoCurrency');
|
||||
|
|
117
cw_core/lib/zano_asset.dart
Normal file
117
cw_core/lib/zano_asset.dart
Normal file
|
@ -0,0 +1,117 @@
|
|||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/hive_type_ids.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
|
||||
part 'zano_asset.g.dart';
|
||||
|
||||
@HiveType(typeId: ZanoAsset.typeId)
|
||||
class ZanoAsset extends CryptoCurrency with HiveObjectMixin {
|
||||
@HiveField(0)
|
||||
final String fullName;
|
||||
@HiveField(1)
|
||||
final String ticker;
|
||||
@HiveField(2)
|
||||
final String assetId;
|
||||
@HiveField(3)
|
||||
final int decimalPoint;
|
||||
@HiveField(4, defaultValue: true)
|
||||
bool _enabled;
|
||||
@HiveField(5)
|
||||
final String? iconPath;
|
||||
// @HiveField(6)
|
||||
// final String? tag;
|
||||
@HiveField(6)
|
||||
final String owner;
|
||||
@HiveField(7)
|
||||
final String metaInfo;
|
||||
@HiveField(8)
|
||||
final BigInt currentSupply;
|
||||
@HiveField(9)
|
||||
final bool hiddenSupply;
|
||||
@HiveField(10)
|
||||
final BigInt totalMaxSupply;
|
||||
@HiveField(11)
|
||||
final bool isInGlobalWhitelist;
|
||||
|
||||
bool get enabled => _enabled;
|
||||
|
||||
set enabled(bool value) => _enabled = value;
|
||||
|
||||
ZanoAsset({
|
||||
this.fullName = '',
|
||||
this.ticker = '',
|
||||
required this.assetId,
|
||||
this.decimalPoint = 12,
|
||||
bool enabled = true,
|
||||
this.iconPath,
|
||||
this.owner = defaultOwner,
|
||||
this.metaInfo = '',
|
||||
required this.currentSupply,
|
||||
this.hiddenSupply = false,
|
||||
required this.totalMaxSupply,
|
||||
this.isInGlobalWhitelist = false,
|
||||
}) : _enabled = enabled,
|
||||
super(
|
||||
name: fullName,
|
||||
title: ticker.toUpperCase(),
|
||||
fullName: fullName,
|
||||
tag: 'ZANO',
|
||||
iconPath: iconPath,
|
||||
decimals: decimalPoint,
|
||||
);
|
||||
|
||||
ZanoAsset.copyWith(ZanoAsset other, {String? icon, String? assetId, bool enabled = true})
|
||||
: this.fullName = other.fullName,
|
||||
this.ticker = other.ticker,
|
||||
this.assetId = assetId ?? other.assetId,
|
||||
this.decimalPoint = other.decimalPoint,
|
||||
this._enabled = enabled && other.enabled,
|
||||
this.iconPath = icon,
|
||||
this.currentSupply = other.currentSupply,
|
||||
this.hiddenSupply = other.hiddenSupply,
|
||||
this.metaInfo = other.metaInfo,
|
||||
this.owner = other.owner,
|
||||
this.totalMaxSupply = other.totalMaxSupply,
|
||||
this.isInGlobalWhitelist = other.isInGlobalWhitelist,
|
||||
super(
|
||||
name: other.name,
|
||||
title: other.ticker.toUpperCase(),
|
||||
fullName: other.name,
|
||||
tag: 'ZANO',
|
||||
iconPath: icon,
|
||||
decimals: other.decimalPoint,
|
||||
enabled: enabled,
|
||||
);
|
||||
|
||||
factory ZanoAsset.fromJson(Map<String, dynamic> json, {bool isInGlobalWhitelist = false}) => ZanoAsset(
|
||||
assetId: json['asset_id'] as String? ?? '',
|
||||
currentSupply: bigIntFromDynamic(json['current_supply']),
|
||||
decimalPoint: json['decimal_point'] as int? ?? 12,
|
||||
fullName: json['full_name'] as String? ?? '',
|
||||
hiddenSupply: json['hidden_supply'] as bool? ?? false,
|
||||
metaInfo: json['meta_info'] as String? ?? '',
|
||||
owner: json['owner'] as String? ?? '',
|
||||
ticker: json['ticker'] as String? ?? '',
|
||||
totalMaxSupply: bigIntFromDynamic(json['total_max_supply']),
|
||||
isInGlobalWhitelist: isInGlobalWhitelist,
|
||||
);
|
||||
|
||||
|
||||
|
||||
static const typeId = ZANO_ASSET_TYPE_ID;
|
||||
static const zanoAssetsBoxName = 'zanoAssetsBox';
|
||||
static const defaultOwner = '0000000000000000000000000000000000000000000000000000000000000000';
|
||||
}
|
||||
|
||||
BigInt bigIntFromDynamic(dynamic d) {
|
||||
if (d is int) {
|
||||
return BigInt.from(d);
|
||||
} else if (d is BigInt) {
|
||||
return d;
|
||||
} else if (d == null) {
|
||||
return BigInt.zero;
|
||||
} else {
|
||||
throw 'cannot cast value of type ${d.runtimeType} to BigInt';
|
||||
//return BigInt.zero;
|
||||
}
|
||||
}
|
|
@ -207,6 +207,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.7"
|
||||
decimal:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: decimal
|
||||
sha256: "24a261d5d5c87e86c7651c417a5dbdf8bcd7080dd592533910e8d0505a279f21"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.3"
|
||||
encrypt:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -577,6 +585,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.0"
|
||||
rational:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: rational
|
||||
sha256: cb808fb6f1a839e6fc5f7d8cb3b0a10e1db48b3be102de73938c627f0b636336
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.3"
|
||||
shelf:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
|
@ -19,6 +19,7 @@ dependencies:
|
|||
flutter_mobx: ^2.0.6+1
|
||||
intl: ^0.19.0
|
||||
encrypt: ^5.0.1
|
||||
decimal: ^2.3.3
|
||||
cake_backup:
|
||||
git:
|
||||
url: https://github.com/cake-tech/cake_backup.git
|
||||
|
|
|
@ -78,7 +78,8 @@ abstract class HavenWalletBase
|
|||
privateSpendKey: haven_wallet.getSecretSpendKey(),
|
||||
privateViewKey: haven_wallet.getSecretViewKey(),
|
||||
publicSpendKey: haven_wallet.getPublicSpendKey(),
|
||||
publicViewKey: haven_wallet.getPublicViewKey());
|
||||
publicViewKey: haven_wallet.getPublicViewKey(),
|
||||
passphrase: "");
|
||||
|
||||
haven_wallet.SyncListener? _listener;
|
||||
ReactionDisposer? _onAccountChangeReaction;
|
||||
|
|
|
@ -209,6 +209,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.4"
|
||||
decimal:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: decimal
|
||||
sha256: "24a261d5d5c87e86c7651c417a5dbdf8bcd7080dd592533910e8d0505a279f21"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.3"
|
||||
encrypt:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -571,6 +579,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.0"
|
||||
rational:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: rational
|
||||
sha256: cb808fb6f1a839e6fc5f7d8cb3b0a10e1db48b3be102de73938c627f0b636336
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.3"
|
||||
shelf:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
|
@ -8,6 +8,7 @@ import 'package:cw_monero/api/exceptions/setup_wallet_exception.dart';
|
|||
import 'package:flutter/foundation.dart';
|
||||
import 'package:monero/monero.dart' as monero;
|
||||
import 'package:mutex/mutex.dart';
|
||||
import 'package:polyseed/polyseed.dart';
|
||||
|
||||
bool debugMonero = false;
|
||||
|
||||
|
@ -36,19 +37,34 @@ String getSeed() {
|
|||
// monero.Wallet_setCacheAttribute(wptr!, key: "cakewallet.seed", value: seed);
|
||||
final cakepolyseed =
|
||||
monero.Wallet_getCacheAttribute(wptr!, key: "cakewallet.seed");
|
||||
final cakepassphrase = getPassphrase();
|
||||
|
||||
final weirdPolyseed = monero.Wallet_getPolyseed(wptr!, passphrase: cakepassphrase);
|
||||
if (weirdPolyseed != "") return weirdPolyseed;
|
||||
|
||||
if (cakepolyseed != "") {
|
||||
if (cakepassphrase != "") {
|
||||
try {
|
||||
final lang = PolyseedLang.getByPhrase(cakepassphrase);
|
||||
final coin = PolyseedCoin.POLYSEED_MONERO;
|
||||
final ps = Polyseed.decode(cakepolyseed, lang, coin);
|
||||
final passphrase = getPassphrase();
|
||||
if (ps.isEncrypted || passphrase == "") return ps.encode(lang, coin);
|
||||
ps.crypt(getPassphrase());
|
||||
return ps.encode(lang, coin);
|
||||
} catch (e) {
|
||||
printV(e);
|
||||
}
|
||||
}
|
||||
return cakepolyseed;
|
||||
}
|
||||
final polyseed = monero.Wallet_getPolyseed(wptr!, passphrase: '');
|
||||
if (polyseed != "") {
|
||||
return polyseed;
|
||||
}
|
||||
final legacy = getSeedLegacy("English");
|
||||
final legacy = getSeedLegacy(null);
|
||||
return legacy;
|
||||
}
|
||||
|
||||
String getSeedLegacy(String? language) {
|
||||
var legacy = monero.Wallet_seed(wptr!, seedOffset: '');
|
||||
final cakepassphrase = getPassphrase();
|
||||
var legacy = monero.Wallet_seed(wptr!, seedOffset: cakepassphrase);
|
||||
switch (language) {
|
||||
case "Chinese (Traditional)": language = "Chinese (simplified)"; break;
|
||||
case "Chinese (Simplified)": language = "Chinese (simplified)"; break;
|
||||
|
@ -58,7 +74,7 @@ String getSeedLegacy(String? language) {
|
|||
}
|
||||
if (monero.Wallet_status(wptr!) != 0) {
|
||||
monero.Wallet_setSeedLanguage(wptr!, language: language ?? "English");
|
||||
legacy = monero.Wallet_seed(wptr!, seedOffset: '');
|
||||
legacy = monero.Wallet_seed(wptr!, seedOffset: cakepassphrase);
|
||||
}
|
||||
if (monero.Wallet_status(wptr!) != 0) {
|
||||
final err = monero.Wallet_errorString(wptr!);
|
||||
|
@ -70,6 +86,10 @@ String getSeedLegacy(String? language) {
|
|||
return legacy;
|
||||
}
|
||||
|
||||
String getPassphrase() {
|
||||
return monero.Wallet_getCacheAttribute(wptr!, key: "cakewallet.passphrase");
|
||||
}
|
||||
|
||||
Map<int, Map<int, Map<int, String>>> addressCache = {};
|
||||
|
||||
String getAddress({int accountIndex = 0, int addressIndex = 0}) {
|
||||
|
|
|
@ -95,9 +95,10 @@ bool isWalletExistSync({required String path}) {
|
|||
void restoreWalletFromSeedSync(
|
||||
{required String path,
|
||||
required String password,
|
||||
required String passphrase,
|
||||
required String seed,
|
||||
int nettype = 0,
|
||||
int restoreHeight = 0}) {
|
||||
int restoreHeight = 0}) async {
|
||||
txhistory = null;
|
||||
final newWptr = monero.WalletManager_recoveryWallet(
|
||||
wmPtr,
|
||||
|
@ -105,7 +106,7 @@ void restoreWalletFromSeedSync(
|
|||
password: password,
|
||||
mnemonic: seed,
|
||||
restoreHeight: restoreHeight,
|
||||
seedOffset: '',
|
||||
seedOffset: passphrase,
|
||||
networkType: 0,
|
||||
);
|
||||
|
||||
|
@ -117,7 +118,13 @@ void restoreWalletFromSeedSync(
|
|||
}
|
||||
wptr = newWptr;
|
||||
|
||||
setRefreshFromBlockHeight(height: restoreHeight);
|
||||
|
||||
monero.Wallet_setCacheAttribute(wptr!, key: "cakewallet.passphrase", value: passphrase);
|
||||
|
||||
openedWalletsByPath[path] = wptr!;
|
||||
|
||||
monero.Wallet_store(wptr!);
|
||||
_lastOpenedWallet = path;
|
||||
}
|
||||
|
||||
|
@ -189,6 +196,48 @@ void restoreWalletFromKeysSync(
|
|||
_lastOpenedWallet = path;
|
||||
}
|
||||
|
||||
|
||||
// English only, because normalization.
|
||||
void restoreWalletFromPolyseedWithOffset(
|
||||
{required String path,
|
||||
required String password,
|
||||
required String seed,
|
||||
required String seedOffset,
|
||||
required String language,
|
||||
int nettype = 0}) {
|
||||
|
||||
txhistory = null;
|
||||
final newWptr = monero.WalletManager_createWalletFromPolyseed(
|
||||
wmPtr,
|
||||
path: path,
|
||||
password: password,
|
||||
networkType: nettype,
|
||||
mnemonic: seed,
|
||||
seedOffset: seedOffset,
|
||||
newWallet: true, // safe to remove
|
||||
restoreHeight: 0,
|
||||
kdfRounds: 1,
|
||||
);
|
||||
|
||||
final status = monero.Wallet_status(newWptr);
|
||||
|
||||
if (status != 0) {
|
||||
final err = monero.Wallet_errorString(newWptr);
|
||||
printV("err: $err");
|
||||
throw WalletRestoreFromKeysException(message: err);
|
||||
}
|
||||
|
||||
wptr = newWptr;
|
||||
|
||||
monero.Wallet_setCacheAttribute(wptr!, key: "cakewallet.seed", value: seed);
|
||||
monero.Wallet_setCacheAttribute(wptr!, key: "cakewallet.passphrase", value: seedOffset);
|
||||
monero.Wallet_store(wptr!);
|
||||
storeSync();
|
||||
|
||||
openedWalletsByPath[path] = wptr!;
|
||||
}
|
||||
|
||||
|
||||
void restoreWalletFromSpendKeySync(
|
||||
{required String path,
|
||||
required String password,
|
||||
|
@ -348,11 +397,12 @@ void _createWallet(Map<String, dynamic> args) {
|
|||
void _restoreFromSeed(Map<String, dynamic> args) {
|
||||
final path = args['path'] as String;
|
||||
final password = args['password'] as String;
|
||||
final passphrase = args['passphrase'] as String;
|
||||
final seed = args['seed'] as String;
|
||||
final restoreHeight = args['restoreHeight'] as int;
|
||||
|
||||
restoreWalletFromSeedSync(
|
||||
path: path, password: password, seed: seed, restoreHeight: restoreHeight);
|
||||
path: path, password: password, passphrase: passphrase, seed: seed, restoreHeight: restoreHeight);
|
||||
}
|
||||
|
||||
void _restoreFromKeys(Map<String, dynamic> args) {
|
||||
|
@ -420,12 +470,14 @@ Future<void> createWallet(
|
|||
Future<void> restoreFromSeed(
|
||||
{required String path,
|
||||
required String password,
|
||||
required String passphrase,
|
||||
required String seed,
|
||||
int nettype = 0,
|
||||
int restoreHeight = 0}) async =>
|
||||
_restoreFromSeed({
|
||||
'path': path,
|
||||
'password': password,
|
||||
'passphrase': passphrase,
|
||||
'seed': seed,
|
||||
'nettype': nettype,
|
||||
'restoreHeight': restoreHeight
|
||||
|
|
|
@ -122,13 +122,17 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
|
|||
@override
|
||||
String get password => _password;
|
||||
|
||||
@override
|
||||
String get passphrase => monero_wallet.getPassphrase();
|
||||
|
||||
@override
|
||||
MoneroWalletKeys get keys => MoneroWalletKeys(
|
||||
primaryAddress: monero_wallet.getAddress(accountIndex: 0, addressIndex: 0),
|
||||
privateSpendKey: monero_wallet.getSecretSpendKey(),
|
||||
privateViewKey: monero_wallet.getSecretViewKey(),
|
||||
publicSpendKey: monero_wallet.getPublicSpendKey(),
|
||||
publicViewKey: monero_wallet.getPublicViewKey());
|
||||
publicViewKey: monero_wallet.getPublicViewKey(),
|
||||
passphrase: monero_wallet.getPassphrase());
|
||||
|
||||
int? get restoreHeight =>
|
||||
transactionHistory.transactions.values.firstOrNull?.height;
|
||||
|
|
|
@ -11,6 +11,8 @@ import 'package:cw_core/wallet_credentials.dart';
|
|||
import 'package:cw_core/wallet_info.dart';
|
||||
import 'package:cw_core/wallet_service.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:cw_monero/api/exceptions/wallet_opening_exception.dart';
|
||||
import 'package:cw_core/get_height_by_date.dart';
|
||||
import 'package:cw_monero/api/account_list.dart';
|
||||
import 'package:cw_monero/api/wallet_manager.dart' as monero_wallet_manager;
|
||||
import 'package:cw_monero/api/wallet_manager.dart';
|
||||
|
@ -42,10 +44,15 @@ class MoneroRestoreWalletFromHardwareCredentials extends WalletCredentials {
|
|||
|
||||
class MoneroRestoreWalletFromSeedCredentials extends WalletCredentials {
|
||||
MoneroRestoreWalletFromSeedCredentials(
|
||||
{required String name, required this.mnemonic, int height = 0, String? password})
|
||||
{required String name,
|
||||
required this.mnemonic,
|
||||
required this.passphrase,
|
||||
int height = 0,
|
||||
String? password})
|
||||
: super(name: name, password: password, height: height);
|
||||
|
||||
final String mnemonic;
|
||||
final String passphrase;
|
||||
}
|
||||
|
||||
class MoneroWalletLoadingException implements Exception {
|
||||
|
@ -94,12 +101,14 @@ class MoneroWalletService extends WalletService<
|
|||
final polyseed = Polyseed.create();
|
||||
final lang = PolyseedLang.getByEnglishName(credentials.language);
|
||||
|
||||
if (credentials.passphrase != null) polyseed.crypt(credentials.passphrase!);
|
||||
|
||||
final heightOverride =
|
||||
getMoneroHeigthByDate(date: DateTime.now().subtract(Duration(days: 2)));
|
||||
|
||||
return _restoreFromPolyseed(
|
||||
path, credentials.password!, polyseed, credentials.walletInfo!, lang,
|
||||
overrideHeight: heightOverride);
|
||||
overrideHeight: heightOverride, passphrase: credentials.passphrase);
|
||||
}
|
||||
|
||||
await monero_wallet_manager.createWallet(
|
||||
|
@ -292,6 +301,7 @@ class MoneroWalletService extends WalletService<
|
|||
await monero_wallet_manager.restoreFromSeed(
|
||||
path: path,
|
||||
password: credentials.password!,
|
||||
passphrase: credentials.passphrase,
|
||||
seed: credentials.mnemonic,
|
||||
restoreHeight: credentials.height!);
|
||||
final wallet = MoneroWallet(
|
||||
|
@ -318,7 +328,8 @@ class MoneroWalletService extends WalletService<
|
|||
Polyseed.decode(credentials.mnemonic, lang, polyseedCoin);
|
||||
|
||||
return _restoreFromPolyseed(
|
||||
path, credentials.password!, polyseed, credentials.walletInfo!, lang);
|
||||
path, credentials.password!, polyseed, credentials.walletInfo!, lang,
|
||||
passphrase: credentials.passphrase);
|
||||
} catch (e) {
|
||||
// TODO: Implement Exception for wallet list service.
|
||||
printV('MoneroWalletsManager Error: $e');
|
||||
|
@ -326,9 +337,35 @@ class MoneroWalletService extends WalletService<
|
|||
}
|
||||
}
|
||||
|
||||
Future<MoneroWallet> _restoreFromPolyseed(String path, String password, Polyseed polyseed,
|
||||
WalletInfo walletInfo, PolyseedLang lang,
|
||||
{PolyseedCoin coin = PolyseedCoin.POLYSEED_MONERO, int? overrideHeight}) async {
|
||||
Future<MoneroWallet> _restoreFromPolyseed(
|
||||
String path, String password, Polyseed polyseed, WalletInfo walletInfo, PolyseedLang lang,
|
||||
{PolyseedCoin coin = PolyseedCoin.POLYSEED_MONERO,
|
||||
int? overrideHeight,
|
||||
String? passphrase}) async {
|
||||
|
||||
if (polyseed.isEncrypted == false &&
|
||||
(passphrase??'') != "") {
|
||||
// Fallback to the different passphrase offset method, when a passphrase
|
||||
// was provided but the polyseed is not encrypted.
|
||||
monero_wallet_manager.restoreWalletFromPolyseedWithOffset(
|
||||
path: path,
|
||||
password: password,
|
||||
seed: polyseed.encode(lang, coin),
|
||||
seedOffset: passphrase??'',
|
||||
language: "English");
|
||||
|
||||
final wallet = MoneroWallet(
|
||||
walletInfo: walletInfo,
|
||||
unspentCoinsInfo: unspentCoinsInfoSource,
|
||||
password: password,
|
||||
);
|
||||
await wallet.init();
|
||||
|
||||
return wallet;
|
||||
}
|
||||
|
||||
if (polyseed.isEncrypted) polyseed.crypt(passphrase ?? '');
|
||||
|
||||
final height = overrideHeight ??
|
||||
getMoneroHeigthByDate(date: DateTime.fromMillisecondsSinceEpoch(polyseed.birthday * 1000));
|
||||
final spendKey = polyseed.generateKey(coin, 32).toHexString();
|
||||
|
|
|
@ -1 +1 @@
|
|||
/Users/omarhatem/.pub-cache/hosted/pub.dev/path_provider_linux-2.2.1/
|
||||
/home/parallels/.pub-cache/hosted/pub.dev/path_provider_linux-2.2.1/
|
|
@ -225,6 +225,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.10"
|
||||
decimal:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: decimal
|
||||
sha256: "24a261d5d5c87e86c7651c417a5dbdf8bcd7080dd592533910e8d0505a279f21"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.3"
|
||||
encrypt:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -503,8 +511,8 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
path: "impls/monero.dart"
|
||||
ref: af5277f96073917185864d3596e82b67bee54e78
|
||||
resolved-ref: af5277f96073917185864d3596e82b67bee54e78
|
||||
ref: 9526921acb774b523a2e1d9ba9a7b389acfc6b70
|
||||
resolved-ref: 9526921acb774b523a2e1d9ba9a7b389acfc6b70
|
||||
url: "https://github.com/mrcyjanek/monero_c"
|
||||
source: git
|
||||
version: "0.0.0"
|
||||
|
@ -660,6 +668,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.0"
|
||||
rational:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: rational
|
||||
sha256: cb808fb6f1a839e6fc5f7d8cb3b0a10e1db48b3be102de73938c627f0b636336
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.3"
|
||||
rxdart:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
|
@ -25,8 +25,7 @@ dependencies:
|
|||
monero:
|
||||
git:
|
||||
url: https://github.com/mrcyjanek/monero_c
|
||||
ref: af5277f96073917185864d3596e82b67bee54e78
|
||||
# ref: 6eb571ea498ed7b854934785f00fabfd0dadf75b # monero_c hash
|
||||
ref: 9526921acb774b523a2e1d9ba9a7b389acfc6b70
|
||||
path: impls/monero.dart
|
||||
mutex: ^3.1.0
|
||||
ledger_flutter_plus: ^1.4.1
|
||||
|
|
|
@ -10,7 +10,7 @@ import 'package:cw_wownero/exceptions/wownero_transaction_creation_exception.dar
|
|||
import 'package:ffi/ffi.dart';
|
||||
import 'package:monero/wownero.dart' as wownero;
|
||||
import 'package:monero/src/generated_bindings_wownero.g.dart' as wownero_gen;
|
||||
|
||||
import 'package:mutex/mutex.dart';
|
||||
|
||||
String getTxKey(String txId) {
|
||||
final ret = wownero.Wallet_getTxKey(wptr!, txid: txId);
|
||||
|
@ -18,6 +18,7 @@ String getTxKey(String txId) {
|
|||
return ret;
|
||||
}
|
||||
|
||||
final txHistoryMutex = Mutex();
|
||||
wownero.TransactionHistory? txhistory;
|
||||
|
||||
bool isRefreshingTx = false;
|
||||
|
@ -26,22 +27,25 @@ Future<void> refreshTransactions() async {
|
|||
isRefreshingTx = true;
|
||||
txhistory ??= wownero.Wallet_history(wptr!);
|
||||
final ptr = txhistory!.address;
|
||||
await txHistoryMutex.acquire();
|
||||
await Isolate.run(() {
|
||||
wownero.TransactionHistory_refresh(Pointer.fromAddress(ptr));
|
||||
});
|
||||
txHistoryMutex.release();
|
||||
isRefreshingTx = false;
|
||||
}
|
||||
|
||||
int countOfTransactions() => wownero.TransactionHistory_count(txhistory!);
|
||||
|
||||
List<Transaction> getAllTransactions() {
|
||||
Future<List<Transaction>> getAllTransactions() async {
|
||||
List<Transaction> dummyTxs = [];
|
||||
|
||||
await txHistoryMutex.acquire();
|
||||
txhistory ??= wownero.Wallet_history(wptr!);
|
||||
wownero.TransactionHistory_refresh(txhistory!);
|
||||
int size = countOfTransactions();
|
||||
final list = List.generate(size, (index) => Transaction(txInfo: wownero.TransactionHistory_transaction(txhistory!, index: index)));
|
||||
|
||||
txHistoryMutex.release();
|
||||
|
||||
final accts = wownero.Wallet_numSubaddressAccounts(wptr!);
|
||||
for (var i = 0; i < accts; i++) {
|
||||
final fullBalance = wownero.Wallet_balance(wptr!, accountIndex: i);
|
||||
|
|
|
@ -7,6 +7,7 @@ import 'package:cw_wownero/api/account_list.dart';
|
|||
import 'package:cw_wownero/api/exceptions/setup_wallet_exception.dart';
|
||||
import 'package:monero/wownero.dart' as wownero;
|
||||
import 'package:mutex/mutex.dart';
|
||||
import 'package:polyseed/polyseed.dart';
|
||||
|
||||
int getSyncingHeight() {
|
||||
// final height = wownero.WOWNERO_cw_WalletListener_height(getWlptr());
|
||||
|
@ -32,22 +33,37 @@ bool isNewTransactionExist() {
|
|||
String getFilename() => wownero.Wallet_filename(wptr!);
|
||||
|
||||
String getSeed() {
|
||||
// wownero.Wallet_setCacheAttribute(wptr!, key: "cakewallet.seed", value: seed);
|
||||
// monero.Wallet_setCacheAttribute(wptr!, key: "cakewallet.seed", value: seed);
|
||||
final cakepolyseed =
|
||||
wownero.Wallet_getCacheAttribute(wptr!, key: "cakewallet.seed");
|
||||
final cakepassphrase = getPassphrase();
|
||||
|
||||
final weirdPolyseed = wownero.Wallet_getPolyseed(wptr!, passphrase: cakepassphrase);
|
||||
if (weirdPolyseed != "") return weirdPolyseed;
|
||||
|
||||
if (cakepolyseed != "") {
|
||||
if (cakepassphrase != "") {
|
||||
try {
|
||||
final lang = PolyseedLang.getByPhrase(cakepassphrase);
|
||||
final coin = PolyseedCoin.POLYSEED_WOWNERO;
|
||||
final ps = Polyseed.decode(cakepolyseed, lang, coin);
|
||||
final passphrase = getPassphrase();
|
||||
if (ps.isEncrypted || passphrase == "") return ps.encode(lang, coin);
|
||||
ps.crypt(passphrase);
|
||||
return ps.encode(lang, coin);
|
||||
} catch (e) {
|
||||
printV(e);
|
||||
}
|
||||
}
|
||||
return cakepolyseed;
|
||||
}
|
||||
final polyseed = wownero.Wallet_getPolyseed(wptr!, passphrase: '');
|
||||
if (polyseed != "") {
|
||||
return polyseed;
|
||||
}
|
||||
final legacy = getSeedLegacy(null);
|
||||
return legacy;
|
||||
}
|
||||
|
||||
String getSeedLegacy(String? language) {
|
||||
var legacy = wownero.Wallet_seed(wptr!, seedOffset: '');
|
||||
final cakepassphrase = getPassphrase();
|
||||
var legacy = wownero.Wallet_seed(wptr!, seedOffset: cakepassphrase);
|
||||
switch (language) {
|
||||
case "Chinese (Traditional)": language = "Chinese (simplified)"; break;
|
||||
case "Chinese (Simplified)": language = "Chinese (simplified)"; break;
|
||||
|
@ -57,7 +73,7 @@ String getSeedLegacy(String? language) {
|
|||
}
|
||||
if (wownero.Wallet_status(wptr!) != 0) {
|
||||
wownero.Wallet_setSeedLanguage(wptr!, language: language ?? "English");
|
||||
legacy = wownero.Wallet_seed(wptr!, seedOffset: '');
|
||||
legacy = wownero.Wallet_seed(wptr!, seedOffset: cakepassphrase);
|
||||
}
|
||||
if (wownero.Wallet_status(wptr!) != 0) {
|
||||
final err = wownero.Wallet_errorString(wptr!);
|
||||
|
@ -70,6 +86,10 @@ String getSeedLegacy(String? language) {
|
|||
}
|
||||
Map<int, Map<int, Map<int, String>>> addressCache = {};
|
||||
|
||||
String getPassphrase() {
|
||||
return wownero.Wallet_getCacheAttribute(wptr!, key: "cakewallet.passphrase");
|
||||
}
|
||||
|
||||
String getAddress({int accountIndex = 0, int addressIndex = 1}) {
|
||||
while (wownero.Wallet_numSubaddresses(wptr!, accountIndex: accountIndex)-1 < addressIndex) {
|
||||
printV("adding subaddress");
|
||||
|
@ -333,4 +353,4 @@ String signMessage(String message, {String address = ""}) {
|
|||
|
||||
bool verifyMessage(String message, String address, String signature) {
|
||||
return wownero.Wallet_verifySignedMessage(wptr!, message: message, address: address, signature: signature);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -90,6 +90,7 @@ bool isWalletExistSync({required String path}) {
|
|||
void restoreWalletFromSeedSync(
|
||||
{required String path,
|
||||
required String password,
|
||||
required String passphrase,
|
||||
required String seed,
|
||||
int nettype = 0,
|
||||
int restoreHeight = 0}) {
|
||||
|
@ -102,10 +103,12 @@ void restoreWalletFromSeedSync(
|
|||
language: seed, // I KNOW - this is supposed to be called seed
|
||||
networkType: 0,
|
||||
);
|
||||
|
||||
final oldwptr = wptr;
|
||||
wptr = newWptr;
|
||||
setRefreshFromBlockHeight(
|
||||
height: wownero.WOWNERO_deprecated_14WordSeedHeight(seed: seed),
|
||||
);
|
||||
wptr = oldwptr;
|
||||
} else {
|
||||
txhistory = null;
|
||||
newWptr = wownero.WalletManager_recoveryWallet(
|
||||
|
@ -114,7 +117,7 @@ void restoreWalletFromSeedSync(
|
|||
password: password,
|
||||
mnemonic: seed,
|
||||
restoreHeight: restoreHeight,
|
||||
seedOffset: '',
|
||||
seedOffset: passphrase,
|
||||
networkType: 0,
|
||||
);
|
||||
}
|
||||
|
@ -127,8 +130,13 @@ void restoreWalletFromSeedSync(
|
|||
}
|
||||
|
||||
wptr = newWptr;
|
||||
|
||||
|
||||
wownero.Wallet_setCacheAttribute(wptr!, key: "cakewallet.passphrase", value: passphrase);
|
||||
wownero.Wallet_setCacheAttribute(wptr!, key: "cakewallet.seed", value: seed);
|
||||
|
||||
openedWalletsByPath[path] = wptr!;
|
||||
|
||||
store();
|
||||
}
|
||||
|
||||
void restoreWalletFromKeysSync(
|
||||
|
@ -196,6 +204,48 @@ void restoreWalletFromKeysSync(
|
|||
openedWalletsByPath[path] = wptr!;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// English only, because normalization.
|
||||
void restoreWalletFromPolyseedWithOffset(
|
||||
{required String path,
|
||||
required String password,
|
||||
required String seed,
|
||||
required String seedOffset,
|
||||
required String language,
|
||||
int nettype = 0}) {
|
||||
|
||||
txhistory = null;
|
||||
final newWptr = wownero.WalletManager_createWalletFromPolyseed(
|
||||
wmPtr,
|
||||
path: path,
|
||||
password: password,
|
||||
networkType: nettype,
|
||||
mnemonic: seed,
|
||||
seedOffset: seedOffset,
|
||||
newWallet: true, // safe to remove
|
||||
restoreHeight: 0,
|
||||
kdfRounds: 1,
|
||||
);
|
||||
|
||||
final status = wownero.Wallet_status(newWptr);
|
||||
|
||||
if (status != 0) {
|
||||
final err = wownero.Wallet_errorString(newWptr);
|
||||
printV("err: $err");
|
||||
throw WalletRestoreFromKeysException(message: err);
|
||||
}
|
||||
|
||||
wptr = newWptr;
|
||||
|
||||
wownero.Wallet_setCacheAttribute(wptr!, key: "cakewallet.seed", value: seed);
|
||||
wownero.Wallet_setCacheAttribute(wptr!, key: "cakewallet.passphrase", value: seedOffset);
|
||||
|
||||
storeSync();
|
||||
|
||||
openedWalletsByPath[path] = wptr!;
|
||||
}
|
||||
|
||||
void restoreWalletFromSpendKeySync(
|
||||
{required String path,
|
||||
required String password,
|
||||
|
@ -319,11 +369,12 @@ void _createWallet(Map<String, dynamic> args) {
|
|||
void _restoreFromSeed(Map<String, dynamic> args) {
|
||||
final path = args['path'] as String;
|
||||
final password = args['password'] as String;
|
||||
final passphrase = args['passphrase'] as String;
|
||||
final seed = args['seed'] as String;
|
||||
final restoreHeight = args['restoreHeight'] as int;
|
||||
|
||||
restoreWalletFromSeedSync(
|
||||
path: path, password: password, seed: seed, restoreHeight: restoreHeight);
|
||||
path: path, password: password, passphrase: passphrase, seed: seed, restoreHeight: restoreHeight);
|
||||
}
|
||||
|
||||
void _restoreFromKeys(Map<String, dynamic> args) {
|
||||
|
@ -391,12 +442,14 @@ Future<void> createWallet(
|
|||
Future<void> restoreFromSeed(
|
||||
{required String path,
|
||||
required String password,
|
||||
required String passphrase,
|
||||
required String seed,
|
||||
int nettype = 0,
|
||||
int restoreHeight = 0}) async =>
|
||||
_restoreFromSeed({
|
||||
'path': path,
|
||||
'password': password,
|
||||
'passphrase': passphrase,
|
||||
'seed': seed,
|
||||
'nettype': nettype,
|
||||
'restoreHeight': restoreHeight
|
||||
|
|
|
@ -117,6 +117,9 @@ abstract class WowneroWalletBase
|
|||
|
||||
String get password => _password;
|
||||
|
||||
@override
|
||||
String get passphrase => wownero_wallet.getPassphrase();
|
||||
|
||||
String _password;
|
||||
|
||||
@override
|
||||
|
@ -125,7 +128,8 @@ abstract class WowneroWalletBase
|
|||
privateSpendKey: wownero_wallet.getSecretSpendKey(),
|
||||
privateViewKey: wownero_wallet.getSecretViewKey(),
|
||||
publicSpendKey: wownero_wallet.getPublicSpendKey(),
|
||||
publicViewKey: wownero_wallet.getPublicViewKey());
|
||||
publicViewKey: wownero_wallet.getPublicViewKey(),
|
||||
passphrase: wownero_wallet.getPassphrase());
|
||||
|
||||
wownero_wallet.SyncListener? _listener;
|
||||
ReactionDisposer? _onAccountChangeReaction;
|
||||
|
@ -557,7 +561,7 @@ abstract class WowneroWalletBase
|
|||
@override
|
||||
Future<Map<String, WowneroTransactionInfo>> fetchTransactions() async {
|
||||
transaction_history.refreshTransactions();
|
||||
return _getAllTransactionsOfAccount(walletAddresses.account?.id)
|
||||
return (await _getAllTransactionsOfAccount(walletAddresses.account?.id))
|
||||
.fold<Map<String, WowneroTransactionInfo>>(<String, WowneroTransactionInfo>{},
|
||||
(Map<String, WowneroTransactionInfo> acc, WowneroTransactionInfo tx) {
|
||||
acc[tx.id] = tx;
|
||||
|
@ -586,9 +590,9 @@ abstract class WowneroWalletBase
|
|||
String getSubaddressLabel(int accountIndex, int addressIndex) =>
|
||||
wownero_wallet.getSubaddressLabel(accountIndex, addressIndex);
|
||||
|
||||
List<WowneroTransactionInfo> _getAllTransactionsOfAccount(int? accountIndex) =>
|
||||
transaction_history
|
||||
.getAllTransactions()
|
||||
Future<List<WowneroTransactionInfo>> _getAllTransactionsOfAccount(int? accountIndex) async =>
|
||||
(await transaction_history
|
||||
.getAllTransactions())
|
||||
.map(
|
||||
(row) => WowneroTransactionInfo(
|
||||
row.hash,
|
||||
|
|
|
@ -30,10 +30,11 @@ class WowneroNewWalletCredentials extends WalletCredentials {
|
|||
|
||||
class WowneroRestoreWalletFromSeedCredentials extends WalletCredentials {
|
||||
WowneroRestoreWalletFromSeedCredentials(
|
||||
{required String name, required this.mnemonic, int height = 0, String? password})
|
||||
{required String name, required this.mnemonic, required this.passphrase, int height = 0, String? password})
|
||||
: super(name: name, password: password, height: height);
|
||||
|
||||
final String mnemonic;
|
||||
final String passphrase;
|
||||
}
|
||||
|
||||
class WowneroWalletLoadingException implements Exception {
|
||||
|
@ -83,12 +84,14 @@ class WowneroWalletService extends WalletService<
|
|||
final polyseed = Polyseed.create();
|
||||
final lang = PolyseedLang.getByEnglishName(credentials.language);
|
||||
|
||||
if (credentials.passphrase != null) polyseed.crypt(credentials.passphrase!);
|
||||
|
||||
final heightOverride =
|
||||
getWowneroHeightByDate(date: DateTime.now().subtract(Duration(days: 2)));
|
||||
|
||||
return _restoreFromPolyseed(
|
||||
path, credentials.password!, polyseed, credentials.walletInfo!, lang,
|
||||
overrideHeight: heightOverride);
|
||||
overrideHeight: heightOverride, passphrase: credentials.passphrase);
|
||||
}
|
||||
|
||||
await wownero_wallet_manager.createWallet(
|
||||
|
@ -266,6 +269,7 @@ class WowneroWalletService extends WalletService<
|
|||
await wownero_wallet_manager.restoreFromSeed(
|
||||
path: path,
|
||||
password: credentials.password!,
|
||||
passphrase: credentials.passphrase,
|
||||
seed: credentials.mnemonic,
|
||||
restoreHeight: credentials.height!);
|
||||
final wallet = WowneroWallet(
|
||||
|
@ -289,7 +293,7 @@ class WowneroWalletService extends WalletService<
|
|||
final polyseed = Polyseed.decode(credentials.mnemonic, lang, polyseedCoin);
|
||||
|
||||
return _restoreFromPolyseed(
|
||||
path, credentials.password!, polyseed, credentials.walletInfo!, lang);
|
||||
path, credentials.password!, polyseed, credentials.walletInfo!, lang, passphrase: credentials.passphrase);
|
||||
} catch (e) {
|
||||
// TODO: Implement Exception for wallet list service.
|
||||
printV('WowneroWalletsManager Error: $e');
|
||||
|
@ -299,7 +303,32 @@ class WowneroWalletService extends WalletService<
|
|||
|
||||
Future<WowneroWallet> _restoreFromPolyseed(
|
||||
String path, String password, Polyseed polyseed, WalletInfo walletInfo, PolyseedLang lang,
|
||||
{PolyseedCoin coin = PolyseedCoin.POLYSEED_WOWNERO, int? overrideHeight}) async {
|
||||
{PolyseedCoin coin = PolyseedCoin.POLYSEED_WOWNERO, int? overrideHeight, String? passphrase}) async {
|
||||
|
||||
|
||||
if (polyseed.isEncrypted == false &&
|
||||
(passphrase??'') != "") {
|
||||
// Fallback to the different passphrase offset method, when a passphrase
|
||||
// was provided but the polyseed is not encrypted.
|
||||
wownero_wallet_manager.restoreWalletFromPolyseedWithOffset(
|
||||
path: path,
|
||||
password: password,
|
||||
seed: polyseed.encode(lang, coin),
|
||||
seedOffset: passphrase??'',
|
||||
language: "English");
|
||||
|
||||
final wallet = WowneroWallet(
|
||||
walletInfo: walletInfo,
|
||||
unspentCoinsInfo: unspentCoinsInfoSource,
|
||||
password: password,
|
||||
);
|
||||
await wallet.init();
|
||||
|
||||
return wallet;
|
||||
}
|
||||
|
||||
if (polyseed.isEncrypted) polyseed.crypt(passphrase ?? '');
|
||||
|
||||
final height = overrideHeight ??
|
||||
getWowneroHeightByDate(date: DateTime.fromMillisecondsSinceEpoch(polyseed.birthday * 1000));
|
||||
final spendKey = polyseed.generateKey(coin, 32).toHexString();
|
||||
|
|
|
@ -209,6 +209,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.4"
|
||||
decimal:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: decimal
|
||||
sha256: "24a261d5d5c87e86c7651c417a5dbdf8bcd7080dd592533910e8d0505a279f21"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.3"
|
||||
encrypt:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -463,8 +471,8 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
path: "impls/monero.dart"
|
||||
ref: af5277f96073917185864d3596e82b67bee54e78
|
||||
resolved-ref: af5277f96073917185864d3596e82b67bee54e78
|
||||
ref: 9526921acb774b523a2e1d9ba9a7b389acfc6b70
|
||||
resolved-ref: 9526921acb774b523a2e1d9ba9a7b389acfc6b70
|
||||
url: "https://github.com/mrcyjanek/monero_c"
|
||||
source: git
|
||||
version: "0.0.0"
|
||||
|
@ -612,6 +620,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.0"
|
||||
rational:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: rational
|
||||
sha256: cb808fb6f1a839e6fc5f7d8cb3b0a10e1db48b3be102de73938c627f0b636336
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.3"
|
||||
shelf:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
|
@ -25,8 +25,7 @@ dependencies:
|
|||
monero:
|
||||
git:
|
||||
url: https://github.com/mrcyjanek/monero_c
|
||||
ref: af5277f96073917185864d3596e82b67bee54e78
|
||||
# ref: 6eb571ea498ed7b854934785f00fabfd0dadf75b # monero_c hash
|
||||
ref: 9526921acb774b523a2e1d9ba9a7b389acfc6b70 # monero_c hash
|
||||
path: impls/monero.dart
|
||||
mutex: ^3.1.0
|
||||
|
||||
|
|
7
cw_zano/.gitignore
vendored
Normal file
7
cw_zano/.gitignore
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
.DS_Store
|
||||
.dart_tool/
|
||||
|
||||
.packages
|
||||
.pub/
|
||||
|
||||
build/
|
10
cw_zano/.metadata
Normal file
10
cw_zano/.metadata
Normal file
|
@ -0,0 +1,10 @@
|
|||
# This file tracks properties of this Flutter project.
|
||||
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
||||
#
|
||||
# This file should be version controlled and should not be manually edited.
|
||||
|
||||
version:
|
||||
revision: 4d7946a68d26794349189cf21b3f68cc6fe61dcb
|
||||
channel: stable
|
||||
|
||||
project_type: plugin
|
3
cw_zano/CHANGELOG.md
Normal file
3
cw_zano/CHANGELOG.md
Normal file
|
@ -0,0 +1,3 @@
|
|||
## 0.0.1
|
||||
|
||||
* TODO: Describe initial release.
|
1
cw_zano/LICENSE
Normal file
1
cw_zano/LICENSE
Normal file
|
@ -0,0 +1 @@
|
|||
TODO: Add your license here.
|
15
cw_zano/README.md
Normal file
15
cw_zano/README.md
Normal file
|
@ -0,0 +1,15 @@
|
|||
# cw_zano
|
||||
|
||||
A new flutter plugin project.
|
||||
|
||||
## Getting Started
|
||||
|
||||
This project is a starting point for a Flutter
|
||||
[plug-in package](https://flutter.dev/developing-packages/),
|
||||
a specialized package that includes platform-specific implementation code for
|
||||
Android and/or iOS.
|
||||
|
||||
For help getting started with Flutter, view our
|
||||
[online documentation](https://flutter.dev/docs), which offers tutorials,
|
||||
samples, guidance on mobile development, and a full API reference.
|
||||
|
6
cw_zano/lib/api/consts.dart
Normal file
6
cw_zano/lib/api/consts.dart
Normal file
|
@ -0,0 +1,6 @@
|
|||
class Consts {
|
||||
static const errorWrongSeed = 'WRONG_SEED';
|
||||
static const errorAlreadyExists = 'ALREADY_EXISTS';
|
||||
static const errorWalletWrongId = 'WALLET_WRONG_ID';
|
||||
static const errorBusy = 'BUSY';
|
||||
}
|
9
cw_zano/lib/api/model/asset_id_params.dart
Normal file
9
cw_zano/lib/api/model/asset_id_params.dart
Normal file
|
@ -0,0 +1,9 @@
|
|||
class AssetIdParams {
|
||||
final String assetId;
|
||||
|
||||
AssetIdParams({required this.assetId});
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'asset_id': assetId,
|
||||
};
|
||||
}
|
32
cw_zano/lib/api/model/balance.dart
Normal file
32
cw_zano/lib/api/model/balance.dart
Normal file
|
@ -0,0 +1,32 @@
|
|||
import 'package:cw_core/zano_asset.dart';
|
||||
import 'package:cw_zano/model/zano_asset.dart';
|
||||
import 'package:cw_zano/zano_formatter.dart';
|
||||
|
||||
class Balance {
|
||||
final ZanoAsset assetInfo;
|
||||
final BigInt awaitingIn;
|
||||
final BigInt awaitingOut;
|
||||
final BigInt total;
|
||||
final BigInt unlocked;
|
||||
|
||||
Balance(
|
||||
{required this.assetInfo,
|
||||
required this.awaitingIn,
|
||||
required this.awaitingOut,
|
||||
required this.total,
|
||||
required this.unlocked});
|
||||
|
||||
String get assetId => assetInfo.assetId;
|
||||
|
||||
@override
|
||||
String toString() => '$assetInfo: $total/$unlocked';
|
||||
|
||||
factory Balance.fromJson(Map<String, dynamic> json) => Balance(
|
||||
assetInfo:
|
||||
ZanoAsset.fromJson(json['asset_info'] as Map<String, dynamic>? ?? {}),
|
||||
awaitingIn: ZanoFormatter.bigIntFromDynamic(json['awaiting_in']),
|
||||
awaitingOut: ZanoFormatter.bigIntFromDynamic(json['awaiting_out']),
|
||||
total: ZanoFormatter.bigIntFromDynamic(json['total']),
|
||||
unlocked: ZanoFormatter.bigIntFromDynamic(json['unlocked']),
|
||||
);
|
||||
}
|
39
cw_zano/lib/api/model/create_wallet_result.dart
Normal file
39
cw_zano/lib/api/model/create_wallet_result.dart
Normal file
|
@ -0,0 +1,39 @@
|
|||
import 'package:cw_zano/api/model/recent_history.dart';
|
||||
import 'package:cw_zano/api/model/wi.dart';
|
||||
|
||||
class CreateWalletResult {
|
||||
final String name;
|
||||
final String pass;
|
||||
final RecentHistory recentHistory;
|
||||
final bool recovered;
|
||||
final String seed;
|
||||
final int walletFileSize;
|
||||
final int walletId;
|
||||
final int walletLocalBcSize;
|
||||
final Wi wi;
|
||||
|
||||
CreateWalletResult(
|
||||
{required this.name,
|
||||
required this.pass,
|
||||
required this.recentHistory,
|
||||
required this.recovered,
|
||||
required this.seed,
|
||||
required this.walletFileSize,
|
||||
required this.walletId,
|
||||
required this.walletLocalBcSize,
|
||||
required this.wi});
|
||||
|
||||
factory CreateWalletResult.fromJson(Map<String, dynamic> json) =>
|
||||
CreateWalletResult(
|
||||
name: json['name'] as String? ?? '',
|
||||
pass: json['pass'] as String? ?? '',
|
||||
recentHistory: RecentHistory.fromJson(
|
||||
json['recent_history'] as Map<String, dynamic>? ?? {}),
|
||||
recovered: json['recovered'] as bool? ?? false,
|
||||
seed: json['seed'] as String? ?? '',
|
||||
walletFileSize: json['wallet_file_size'] as int? ?? 0,
|
||||
walletId: json['wallet_id'] as int? ?? 0,
|
||||
walletLocalBcSize: json['wallet_local_bc_size'] as int? ?? 0,
|
||||
wi: Wi.fromJson(json['wi'] as Map<String, dynamic>? ?? {}),
|
||||
);
|
||||
}
|
20
cw_zano/lib/api/model/destination.dart
Normal file
20
cw_zano/lib/api/model/destination.dart
Normal file
|
@ -0,0 +1,20 @@
|
|||
class Destination {
|
||||
final BigInt amount; // transfered as string
|
||||
final String address;
|
||||
final String assetId;
|
||||
|
||||
Destination(
|
||||
{required this.amount, required this.address, required this.assetId});
|
||||
|
||||
factory Destination.fromJson(Map<String, dynamic> json) => Destination(
|
||||
amount: BigInt.parse(json['amount'] as String? ?? '0'),
|
||||
address: json['address'] as String? ?? '',
|
||||
assetId: json['asset_id'] as String? ?? '',
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'amount': amount.toString(),
|
||||
'address': address,
|
||||
'asset_id': assetId,
|
||||
};
|
||||
}
|
18
cw_zano/lib/api/model/employed_entries.dart
Normal file
18
cw_zano/lib/api/model/employed_entries.dart
Normal file
|
@ -0,0 +1,18 @@
|
|||
import 'package:cw_zano/api/model/receive.dart';
|
||||
|
||||
class EmployedEntries {
|
||||
final List<Receive> receive;
|
||||
final List<Receive> send;
|
||||
|
||||
EmployedEntries({required this.receive, required this.send});
|
||||
|
||||
factory EmployedEntries.fromJson(Map<String, dynamic> json) =>
|
||||
EmployedEntries(
|
||||
receive: json['receive'] == null ? [] : (json['receive'] as List<dynamic>)
|
||||
.map((e) => Receive.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
send: json['spent'] == null ? [] : (json['spent'] as List<dynamic>)
|
||||
.map((e) => Receive.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
);
|
||||
}
|
16
cw_zano/lib/api/model/get_address_info_result.dart
Normal file
16
cw_zano/lib/api/model/get_address_info_result.dart
Normal file
|
@ -0,0 +1,16 @@
|
|||
class GetAddressInfoResult {
|
||||
final bool valid;
|
||||
final bool auditable;
|
||||
final bool paymentId;
|
||||
final bool wrap;
|
||||
|
||||
GetAddressInfoResult(
|
||||
{required this.valid, required this.auditable, required this.paymentId, required this.wrap});
|
||||
|
||||
factory GetAddressInfoResult.fromJson(Map<String, dynamic> json) => GetAddressInfoResult(
|
||||
valid: json['valid'] as bool? ?? false,
|
||||
auditable: json['auditable'] as bool? ?? false,
|
||||
paymentId: json['payment_id'] as bool? ?? false,
|
||||
wrap: json['wrap'] as bool? ?? false,
|
||||
);
|
||||
}
|
14
cw_zano/lib/api/model/get_recent_txs_and_info_params.dart
Normal file
14
cw_zano/lib/api/model/get_recent_txs_and_info_params.dart
Normal file
|
@ -0,0 +1,14 @@
|
|||
class GetRecentTxsAndInfoParams {
|
||||
final int offset;
|
||||
final int count;
|
||||
final bool updateProvisionInfo;
|
||||
|
||||
GetRecentTxsAndInfoParams({required this.offset, required this.count, this.updateProvisionInfo = true});
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'offset': offset,
|
||||
'count': count,
|
||||
'update_provision_info': updateProvisionInfo,
|
||||
'order': 'FROM_BEGIN_TO_END',
|
||||
};
|
||||
}
|
12
cw_zano/lib/api/model/get_recent_txs_and_info_result.dart
Normal file
12
cw_zano/lib/api/model/get_recent_txs_and_info_result.dart
Normal file
|
@ -0,0 +1,12 @@
|
|||
import 'package:cw_zano/api/model/transfer.dart';
|
||||
|
||||
class GetRecentTxsAndInfoResult {
|
||||
final List<Transfer> transfers;
|
||||
final int lastItemIndex;
|
||||
final int totalTransfers;
|
||||
|
||||
GetRecentTxsAndInfoResult({required this.transfers, required this.lastItemIndex, required this.totalTransfers});
|
||||
|
||||
GetRecentTxsAndInfoResult.empty(): this.transfers = [], this.lastItemIndex = 0, this.totalTransfers = 0;
|
||||
|
||||
}
|
14
cw_zano/lib/api/model/get_wallet_info_result.dart
Normal file
14
cw_zano/lib/api/model/get_wallet_info_result.dart
Normal file
|
@ -0,0 +1,14 @@
|
|||
import 'package:cw_zano/api/model/wi.dart';
|
||||
import 'package:cw_zano/api/model/wi_extended.dart';
|
||||
|
||||
class GetWalletInfoResult {
|
||||
final Wi wi;
|
||||
final WiExtended wiExtended;
|
||||
|
||||
GetWalletInfoResult({required this.wi, required this.wiExtended});
|
||||
|
||||
factory GetWalletInfoResult.fromJson(Map<String, dynamic> json) => GetWalletInfoResult(
|
||||
wi: Wi.fromJson(json['wi'] as Map<String, dynamic>? ?? {}),
|
||||
wiExtended: WiExtended.fromJson(json['wi_extended'] as Map<String, dynamic>? ?? {}),
|
||||
);
|
||||
}
|
26
cw_zano/lib/api/model/get_wallet_status_result.dart
Normal file
26
cw_zano/lib/api/model/get_wallet_status_result.dart
Normal file
|
@ -0,0 +1,26 @@
|
|||
class GetWalletStatusResult {
|
||||
final int currentDaemonHeight;
|
||||
final int currentWalletHeight;
|
||||
final bool isDaemonConnected;
|
||||
final bool isInLongRefresh;
|
||||
final int progress;
|
||||
final int walletState;
|
||||
|
||||
GetWalletStatusResult(
|
||||
{required this.currentDaemonHeight,
|
||||
required this.currentWalletHeight,
|
||||
required this.isDaemonConnected,
|
||||
required this.isInLongRefresh,
|
||||
required this.progress,
|
||||
required this.walletState});
|
||||
|
||||
factory GetWalletStatusResult.fromJson(Map<String, dynamic> json) =>
|
||||
GetWalletStatusResult(
|
||||
currentDaemonHeight: json['current_daemon_height'] as int? ?? 0,
|
||||
currentWalletHeight: json['current_wallet_height'] as int? ?? 0,
|
||||
isDaemonConnected: json['is_daemon_connected'] as bool? ?? false,
|
||||
isInLongRefresh: json['is_in_long_refresh'] as bool? ?? false,
|
||||
progress: json['progress'] as int? ?? 0,
|
||||
walletState: json['wallet_state'] as int? ?? 0,
|
||||
);
|
||||
}
|
13
cw_zano/lib/api/model/proxy_to_daemon_params.dart
Normal file
13
cw_zano/lib/api/model/proxy_to_daemon_params.dart
Normal file
|
@ -0,0 +1,13 @@
|
|||
import 'dart:convert';
|
||||
|
||||
class ProxyToDaemonParams {
|
||||
final String body;
|
||||
final String uri;
|
||||
|
||||
ProxyToDaemonParams({required this.body, required this.uri});
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'base64_body': base64Encode(utf8.encode(body)),
|
||||
'uri': uri,
|
||||
};
|
||||
}
|
13
cw_zano/lib/api/model/proxy_to_daemon_result.dart
Normal file
13
cw_zano/lib/api/model/proxy_to_daemon_result.dart
Normal file
|
@ -0,0 +1,13 @@
|
|||
import 'dart:convert';
|
||||
|
||||
class ProxyToDaemonResult {
|
||||
final String body;
|
||||
final int responseCode;
|
||||
|
||||
ProxyToDaemonResult({required this.body, required this.responseCode});
|
||||
|
||||
factory ProxyToDaemonResult.fromJson(Map<String, dynamic> json) => ProxyToDaemonResult(
|
||||
body: utf8.decode(base64Decode(json['base64_body'] as String? ?? '')),
|
||||
responseCode: json['response_code'] as int? ?? 0,
|
||||
);
|
||||
}
|
15
cw_zano/lib/api/model/receive.dart
Normal file
15
cw_zano/lib/api/model/receive.dart
Normal file
|
@ -0,0 +1,15 @@
|
|||
import 'package:cw_zano/zano_formatter.dart';
|
||||
|
||||
class Receive {
|
||||
final BigInt amount;
|
||||
final String assetId;
|
||||
final int index;
|
||||
|
||||
Receive({required this.amount, required this.assetId, required this.index});
|
||||
|
||||
factory Receive.fromJson(Map<String, dynamic> json) => Receive(
|
||||
amount: ZanoFormatter.bigIntFromDynamic(json['amount']),
|
||||
assetId: json['asset_id'] as String? ?? '',
|
||||
index: json['index'] as int? ?? 0,
|
||||
);
|
||||
}
|
20
cw_zano/lib/api/model/recent_history.dart
Normal file
20
cw_zano/lib/api/model/recent_history.dart
Normal file
|
@ -0,0 +1,20 @@
|
|||
import 'package:cw_zano/api/model/transfer.dart';
|
||||
|
||||
class RecentHistory {
|
||||
final List<Transfer>? history;
|
||||
final int lastItemIndex;
|
||||
final int totalHistoryItems;
|
||||
|
||||
RecentHistory(
|
||||
{required this.history,
|
||||
required this.lastItemIndex,
|
||||
required this.totalHistoryItems});
|
||||
|
||||
factory RecentHistory.fromJson(Map<String, dynamic> json) => RecentHistory(
|
||||
history: json['history'] == null ? null : (json['history'] as List<dynamic>)
|
||||
.map((e) => Transfer.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
lastItemIndex: json['last_item_index'] as int? ?? 0,
|
||||
totalHistoryItems: json['total_history_items'] as int? ?? 0,
|
||||
);
|
||||
}
|
9
cw_zano/lib/api/model/store_result.dart
Normal file
9
cw_zano/lib/api/model/store_result.dart
Normal file
|
@ -0,0 +1,9 @@
|
|||
class StoreResult {
|
||||
final int walletFileSize;
|
||||
|
||||
StoreResult({required this.walletFileSize});
|
||||
|
||||
factory StoreResult.fromJson(Map<String, dynamic> json) => StoreResult(
|
||||
walletFileSize: json['wallet_file_size'] as int? ?? 0,
|
||||
);
|
||||
}
|
16
cw_zano/lib/api/model/subtransfer.dart
Normal file
16
cw_zano/lib/api/model/subtransfer.dart
Normal file
|
@ -0,0 +1,16 @@
|
|||
import 'package:cw_zano/zano_formatter.dart';
|
||||
|
||||
class Subtransfer {
|
||||
final BigInt amount;
|
||||
final String assetId;
|
||||
final bool isIncome;
|
||||
|
||||
Subtransfer(
|
||||
{required this.amount, required this.assetId, required this.isIncome});
|
||||
|
||||
factory Subtransfer.fromJson(Map<String, dynamic> json) => Subtransfer(
|
||||
amount: ZanoFormatter.bigIntFromDynamic(json['amount']),
|
||||
assetId: json['asset_id'] as String? ?? '',
|
||||
isIncome: json['is_income'] as bool? ?? false,
|
||||
);
|
||||
}
|
126
cw_zano/lib/api/model/transfer.dart
Normal file
126
cw_zano/lib/api/model/transfer.dart
Normal file
|
@ -0,0 +1,126 @@
|
|||
import 'package:cw_core/zano_asset.dart';
|
||||
import 'package:cw_zano/api/model/employed_entries.dart';
|
||||
import 'package:cw_zano/api/model/subtransfer.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:cw_zano/model/zano_asset.dart';
|
||||
import 'package:cw_zano/model/zano_transaction_info.dart';
|
||||
import 'package:cw_zano/zano_formatter.dart';
|
||||
import 'package:cw_zano/zano_wallet.dart';
|
||||
import 'package:cw_zano/zano_wallet_api.dart';
|
||||
|
||||
class Transfer {
|
||||
final String comment;
|
||||
final EmployedEntries employedEntries;
|
||||
final int fee;
|
||||
final int height;
|
||||
final bool isMining;
|
||||
final bool isMixing;
|
||||
final bool isService;
|
||||
final String paymentId;
|
||||
final List<String> remoteAddresses;
|
||||
final List<String> remoteAliases;
|
||||
final bool showSender;
|
||||
final List<Subtransfer> subtransfers;
|
||||
final int timestamp;
|
||||
final int transferInternalIndex;
|
||||
final int txBlobSize;
|
||||
final String txHash;
|
||||
final int txType;
|
||||
final int unlockTime;
|
||||
|
||||
Transfer({
|
||||
required this.comment,
|
||||
required this.employedEntries,
|
||||
required this.fee,
|
||||
required this.height,
|
||||
required this.isMining,
|
||||
required this.isMixing,
|
||||
required this.isService,
|
||||
required this.paymentId,
|
||||
required this.remoteAddresses,
|
||||
required this.remoteAliases,
|
||||
required this.showSender,
|
||||
required this.subtransfers,
|
||||
required this.timestamp,
|
||||
required this.transferInternalIndex,
|
||||
required this.txBlobSize,
|
||||
required this.txHash,
|
||||
required this.txType,
|
||||
required this.unlockTime,
|
||||
});
|
||||
|
||||
factory Transfer.fromJson(Map<String, dynamic> json) => Transfer(
|
||||
comment: json['comment'] as String? ?? '',
|
||||
employedEntries: EmployedEntries.fromJson(json['employed_entries'] as Map<String, dynamic>? ?? {}),
|
||||
fee: json['fee'] as int? ?? 0,
|
||||
height: json['height'] as int? ?? 0,
|
||||
isMining: json['is_mining'] as bool? ?? false,
|
||||
isMixing: json['is_mixing'] as bool? ?? false,
|
||||
isService: json['is_service'] as bool? ?? false,
|
||||
paymentId: json['payment_id'] as String? ?? '',
|
||||
remoteAddresses: json['remote_addresses'] == null ? [] : (json['remote_addresses'] as List<dynamic>).cast<String>(),
|
||||
remoteAliases: json['remote_aliases'] == null ? [] : (json['remote_aliases'] as List<dynamic>).cast<String>(),
|
||||
showSender: json['show_sender'] as bool? ?? false,
|
||||
subtransfers: (json['subtransfers'] as List<dynamic>? ?? []).map((e) => Subtransfer.fromJson(e as Map<String, dynamic>)).toList(),
|
||||
timestamp: json['timestamp'] as int? ?? 0,
|
||||
transferInternalIndex: json['transfer_internal_index'] == null
|
||||
? 0
|
||||
: json['transfer_internal_index'] is double
|
||||
? (json['transfer_internal_index'] as double).toInt()
|
||||
: json['transfer_internal_index'] as int,
|
||||
txBlobSize: json['tx_blob_size'] as int? ?? 0,
|
||||
txHash: json['tx_hash'] as String? ?? '',
|
||||
txType: json['tx_type'] as int? ?? 0,
|
||||
unlockTime: json['unlock_time'] as int? ?? 0,
|
||||
);
|
||||
|
||||
static Map<String, ZanoTransactionInfo> makeMap(List<Transfer> transfers, Map<String, ZanoAsset> zanoAssets, int currentDaemonHeight) {
|
||||
return Map.fromIterable(
|
||||
transfers,
|
||||
key: (item) => (item as Transfer).txHash,
|
||||
value: (transfer) {
|
||||
transfer as Transfer;
|
||||
// Simple (only one subtransfer OR two subtransfers and the second is Zano, outgoing and amount equals to fee) or complex?
|
||||
Subtransfer? single = transfer.subtransfers.singleOrNull;
|
||||
if (transfer.subtransfers.length == 2) {
|
||||
final zano = transfer.subtransfers.firstWhereOrNull((element) => element.assetId == ZanoWalletBase.zanoAssetId);
|
||||
if (zano != null && !zano.isIncome && zano.amount == BigInt.from(transfer.fee)) {
|
||||
single = transfer.subtransfers.firstWhere((element) => element.assetId != ZanoWalletBase.zanoAssetId);
|
||||
}
|
||||
}
|
||||
bool isSimple = single != null;
|
||||
// TODO: for complex transactions we show zano or any other transaction, will fix it later
|
||||
if (!isSimple) {
|
||||
single =
|
||||
transfer.subtransfers.firstWhereOrNull((element) => element.assetId == ZanoWalletBase.zanoAssetId) ?? transfer.subtransfers.first;
|
||||
}
|
||||
if (single.assetId != ZanoWalletBase.zanoAssetId) {
|
||||
final asset = zanoAssets[single.assetId];
|
||||
if (asset == null) {
|
||||
ZanoWalletApi.error('unknown asset ${single.assetId}');
|
||||
}
|
||||
final ticker = asset == null ? '***' : asset.ticker;
|
||||
final decimalPoint = asset == null ? ZanoFormatter.defaultDecimalPoint : asset.decimalPoint;
|
||||
return ZanoTransactionInfo.fromTransfer(
|
||||
transfer,
|
||||
confirmations: currentDaemonHeight - transfer.height,
|
||||
isIncome: single.isIncome,
|
||||
assetId: single.assetId,
|
||||
amount: single.amount,
|
||||
tokenSymbol: isSimple ? ticker : '*${ticker}',
|
||||
decimalPoint: decimalPoint,
|
||||
);
|
||||
}
|
||||
final amount = single.isIncome ? single.amount : single.amount - BigInt.from(transfer.fee);
|
||||
return ZanoTransactionInfo.fromTransfer(
|
||||
transfer,
|
||||
confirmations: currentDaemonHeight - transfer.height,
|
||||
isIncome: single.isIncome,
|
||||
assetId: single.assetId,
|
||||
amount: amount,
|
||||
tokenSymbol: isSimple ? 'ZANO' : '*ZANO',
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
41
cw_zano/lib/api/model/transfer_params.dart
Normal file
41
cw_zano/lib/api/model/transfer_params.dart
Normal file
|
@ -0,0 +1,41 @@
|
|||
import 'package:cw_zano/api/model/destination.dart';
|
||||
|
||||
class TransferParams {
|
||||
final List<Destination> destinations;
|
||||
final BigInt fee;
|
||||
final int mixin;
|
||||
final String paymentId;
|
||||
final String comment;
|
||||
final bool pushPayer;
|
||||
final bool hideReceiver;
|
||||
|
||||
TransferParams({
|
||||
required this.destinations,
|
||||
required this.fee,
|
||||
required this.mixin,
|
||||
required this.paymentId,
|
||||
required this.comment,
|
||||
required this.pushPayer,
|
||||
required this.hideReceiver,
|
||||
});
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'destinations': destinations,
|
||||
'fee': fee.toInt(),
|
||||
'mixin': mixin,
|
||||
'payment_id': paymentId,
|
||||
'comment': comment,
|
||||
'push_payer': pushPayer,
|
||||
'hide_receiver': hideReceiver,
|
||||
};
|
||||
|
||||
factory TransferParams.fromJson(Map<String, dynamic> json) => TransferParams(
|
||||
destinations: (json['destinations'] as List<dynamic>?)?.map((e) => Destination.fromJson(e as Map<String, dynamic>)).toList() ?? [],
|
||||
fee: BigInt.from(json['fee'] as int? ?? 0),
|
||||
mixin: json['mixin'] as int? ?? 0,
|
||||
paymentId: json['payment_id'] as String? ?? '',
|
||||
comment: json['comment'] as String? ?? '',
|
||||
pushPayer: json['push_payer'] as bool? ?? false,
|
||||
hideReceiver: json['hide_receiver'] as bool? ?? false,
|
||||
);
|
||||
}
|
13
cw_zano/lib/api/model/transfer_result.dart
Normal file
13
cw_zano/lib/api/model/transfer_result.dart
Normal file
|
@ -0,0 +1,13 @@
|
|||
class TransferResult {
|
||||
final String txHash;
|
||||
final int txSize;
|
||||
final String txUnsignedHex;
|
||||
|
||||
TransferResult({required this.txHash, required this.txSize, required this.txUnsignedHex});
|
||||
|
||||
factory TransferResult.fromJson(Map<String, dynamic> json) => TransferResult(
|
||||
txHash: json['tx_hash'] as String? ?? '',
|
||||
txSize: json['tx_size'] as int? ?? 0,
|
||||
txUnsignedHex: json['tx_unsigned_hex'] as String? ?? '',
|
||||
);
|
||||
}
|
32
cw_zano/lib/api/model/wi.dart
Normal file
32
cw_zano/lib/api/model/wi.dart
Normal file
|
@ -0,0 +1,32 @@
|
|||
import 'package:cw_zano/api/model/balance.dart';
|
||||
|
||||
class Wi {
|
||||
final String address;
|
||||
final List<Balance> balances;
|
||||
final bool isAuditable;
|
||||
final bool isWatchOnly;
|
||||
final int minedTotal;
|
||||
final String path;
|
||||
final String viewSecKey;
|
||||
|
||||
Wi(
|
||||
{required this.address,
|
||||
required this.balances,
|
||||
required this.isAuditable,
|
||||
required this.isWatchOnly,
|
||||
required this.minedTotal,
|
||||
required this.path,
|
||||
required this.viewSecKey});
|
||||
|
||||
factory Wi.fromJson(Map<String, dynamic> json) => Wi(
|
||||
address: json['address'] as String? ?? '',
|
||||
balances: (json['balances'] as List<dynamic>? ?? [])
|
||||
.map((e) => Balance.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
isAuditable: json['is_auditable'] as bool? ?? false,
|
||||
isWatchOnly: json['is_watch_only'] as bool? ?? false,
|
||||
minedTotal: json['mined_total'] as int? ?? 0,
|
||||
path: json['path'] as String? ?? '',
|
||||
viewSecKey: json['view_sec_key'] as String? ?? '',
|
||||
);
|
||||
}
|
17
cw_zano/lib/api/model/wi_extended.dart
Normal file
17
cw_zano/lib/api/model/wi_extended.dart
Normal file
|
@ -0,0 +1,17 @@
|
|||
class WiExtended {
|
||||
final String seed;
|
||||
final String spendPrivateKey;
|
||||
final String spendPublicKey;
|
||||
final String viewPrivateKey;
|
||||
final String viewPublicKey;
|
||||
|
||||
WiExtended({required this.seed, required this.spendPrivateKey, required this.spendPublicKey, required this.viewPrivateKey, required this.viewPublicKey});
|
||||
|
||||
factory WiExtended.fromJson(Map<String, dynamic> json) => WiExtended(
|
||||
seed: json['seed'] as String? ?? '',
|
||||
spendPrivateKey: json['spend_private_key'] as String? ?? '',
|
||||
spendPublicKey: json['spend_public_key'] as String? ?? '',
|
||||
viewPrivateKey: json['view_private_key'] as String? ?? '',
|
||||
viewPublicKey: json['view_public_key'] as String? ?? '',
|
||||
);
|
||||
}
|
1630
cw_zano/lib/mnemonics/english.dart
Normal file
1630
cw_zano/lib/mnemonics/english.dart
Normal file
File diff suppressed because it is too large
Load diff
52
cw_zano/lib/model/pending_zano_transaction.dart
Normal file
52
cw_zano/lib/model/pending_zano_transaction.dart
Normal file
|
@ -0,0 +1,52 @@
|
|||
import 'package:cw_core/pending_transaction.dart';
|
||||
import 'package:cw_zano/api/model/destination.dart';
|
||||
import 'package:cw_zano/api/model/transfer_result.dart';
|
||||
import 'package:cw_zano/zano_formatter.dart';
|
||||
import 'package:cw_zano/zano_wallet.dart';
|
||||
|
||||
class PendingZanoTransaction with PendingTransaction {
|
||||
PendingZanoTransaction({
|
||||
required this.zanoWallet,
|
||||
required this.destinations,
|
||||
required this.fee,
|
||||
required this.comment,
|
||||
required this.assetId,
|
||||
required this.ticker,
|
||||
this.decimalPoint = ZanoFormatter.defaultDecimalPoint,
|
||||
required this.amount,
|
||||
});
|
||||
|
||||
final ZanoWalletBase zanoWallet;
|
||||
final List<Destination> destinations;
|
||||
final BigInt fee;
|
||||
final String comment;
|
||||
final String assetId;
|
||||
final String ticker;
|
||||
final int decimalPoint;
|
||||
final BigInt amount;
|
||||
|
||||
@override
|
||||
String get id => transferResult?.txHash ?? '';
|
||||
|
||||
@override
|
||||
String get hex => '';
|
||||
|
||||
@override
|
||||
String get amountFormatted => '${ZanoFormatter.bigIntAmountToString(amount, decimalPoint)} $ticker';
|
||||
|
||||
@override
|
||||
String get feeFormatted => '${ZanoFormatter.bigIntAmountToString(fee)} ZANO';
|
||||
|
||||
TransferResult? transferResult;
|
||||
|
||||
@override
|
||||
Future<void> commit() async {
|
||||
await zanoWallet.transfer(destinations, fee, comment);
|
||||
zanoWallet.fetchTransactions();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String?> commitUR() {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
}
|
0
cw_zano/lib/model/zano_asset.dart
Normal file
0
cw_zano/lib/model/zano_asset.dart
Normal file
17
cw_zano/lib/model/zano_balance.dart
Normal file
17
cw_zano/lib/model/zano_balance.dart
Normal file
|
@ -0,0 +1,17 @@
|
|||
import 'package:cw_core/balance.dart';
|
||||
import 'package:cw_zano/zano_formatter.dart';
|
||||
|
||||
class ZanoBalance extends Balance {
|
||||
final BigInt total;
|
||||
final BigInt unlocked;
|
||||
final int decimalPoint;
|
||||
ZanoBalance({required this.total, required this.unlocked, this.decimalPoint = ZanoFormatter.defaultDecimalPoint}) : super(unlocked.isValidInt ? unlocked.toInt() : 0, (total - unlocked).isValidInt ? (total - unlocked).toInt() : 0);
|
||||
|
||||
ZanoBalance.empty({this.decimalPoint = ZanoFormatter.defaultDecimalPoint}): total = BigInt.zero, unlocked = BigInt.zero, super(0, 0);
|
||||
|
||||
@override
|
||||
String get formattedAdditionalBalance => ZanoFormatter.bigIntAmountToString(total - unlocked, decimalPoint);
|
||||
|
||||
@override
|
||||
String get formattedAvailableBalance => ZanoFormatter.bigIntAmountToString(unlocked, decimalPoint);
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
class ZanoTransactionCreationException implements Exception {
|
||||
ZanoTransactionCreationException(this.message);
|
||||
|
||||
final String message;
|
||||
|
||||
@override
|
||||
String toString() => message;
|
||||
}
|
11
cw_zano/lib/model/zano_transaction_credentials.dart
Normal file
11
cw_zano/lib/model/zano_transaction_credentials.dart
Normal file
|
@ -0,0 +1,11 @@
|
|||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/monero_transaction_priority.dart';
|
||||
import 'package:cw_core/output_info.dart';
|
||||
|
||||
class ZanoTransactionCredentials {
|
||||
ZanoTransactionCredentials({required this.outputs, required this.priority, required this.currency});
|
||||
|
||||
final List<OutputInfo> outputs;
|
||||
final MoneroTransactionPriority priority;
|
||||
final CryptoCurrency currency;
|
||||
}
|
76
cw_zano/lib/model/zano_transaction_info.dart
Normal file
76
cw_zano/lib/model/zano_transaction_info.dart
Normal file
|
@ -0,0 +1,76 @@
|
|||
import 'package:cw_core/format_amount.dart';
|
||||
import 'package:cw_core/transaction_direction.dart';
|
||||
import 'package:cw_core/transaction_info.dart';
|
||||
import 'package:cw_zano/api/model/transfer.dart';
|
||||
import 'package:cw_zano/zano_formatter.dart';
|
||||
|
||||
class ZanoTransactionInfo extends TransactionInfo {
|
||||
ZanoTransactionInfo({
|
||||
required this.id,
|
||||
required this.height,
|
||||
required this.direction,
|
||||
required this.date,
|
||||
required this.isPending,
|
||||
required this.zanoAmount,
|
||||
required this.fee,
|
||||
required this.confirmations,
|
||||
required this.tokenSymbol,
|
||||
required this.decimalPoint,
|
||||
required String assetId,
|
||||
}) : amount = zanoAmount.isValidInt ? zanoAmount.toInt() : 0 {
|
||||
additionalInfo['assetId'] = assetId;
|
||||
}
|
||||
|
||||
ZanoTransactionInfo.fromTransfer(Transfer transfer,
|
||||
{required int confirmations,
|
||||
required bool isIncome,
|
||||
required String assetId,
|
||||
required BigInt amount,
|
||||
this.tokenSymbol = 'ZANO',
|
||||
this.decimalPoint = ZanoFormatter.defaultDecimalPoint})
|
||||
: id = transfer.txHash,
|
||||
height = transfer.height,
|
||||
direction = isIncome ? TransactionDirection.incoming : TransactionDirection.outgoing,
|
||||
date = DateTime.fromMillisecondsSinceEpoch(transfer.timestamp * 1000),
|
||||
zanoAmount = amount,
|
||||
amount = amount.isValidInt ? amount.toInt() : 0,
|
||||
fee = transfer.fee,
|
||||
confirmations = confirmations,
|
||||
isPending = false,
|
||||
recipientAddress = transfer.remoteAddresses.isNotEmpty ? transfer.remoteAddresses.first : '' {
|
||||
additionalInfo = <String, dynamic>{
|
||||
'comment': transfer.comment,
|
||||
'assetId': assetId,
|
||||
};
|
||||
}
|
||||
String get assetId => additionalInfo["assetId"] as String;
|
||||
set assetId(String newId) => additionalInfo["assetId"] = newId;
|
||||
final String id;
|
||||
final int height;
|
||||
final TransactionDirection direction;
|
||||
final DateTime date;
|
||||
final bool isPending;
|
||||
final BigInt zanoAmount;
|
||||
final int amount;
|
||||
final int fee;
|
||||
final int confirmations;
|
||||
final int decimalPoint;
|
||||
late String recipientAddress;
|
||||
final String tokenSymbol;
|
||||
String? _fiatAmount;
|
||||
String? key;
|
||||
|
||||
@override
|
||||
String amountFormatted() => '${formatAmount(ZanoFormatter.bigIntAmountToString(zanoAmount, decimalPoint))} $tokenSymbol';
|
||||
|
||||
@override
|
||||
String fiatAmount() => _fiatAmount ?? '';
|
||||
|
||||
@override
|
||||
void changeFiatAmount(String amount) => _fiatAmount = formatAmount(amount);
|
||||
|
||||
@override
|
||||
String feeFormatted() => '${formatAmount(ZanoFormatter.intAmountToString(fee))} $feeCurrency';
|
||||
|
||||
String get feeCurrency => 'ZANO';
|
||||
}
|
12
cw_zano/lib/model/zano_wallet_keys.dart
Normal file
12
cw_zano/lib/model/zano_wallet_keys.dart
Normal file
|
@ -0,0 +1,12 @@
|
|||
class ZanoWalletKeys {
|
||||
const ZanoWalletKeys(
|
||||
{required this.privateSpendKey,
|
||||
required this.privateViewKey,
|
||||
required this.publicSpendKey,
|
||||
required this.publicViewKey});
|
||||
|
||||
final String publicViewKey;
|
||||
final String privateViewKey;
|
||||
final String publicSpendKey;
|
||||
final String privateSpendKey;
|
||||
}
|
74
cw_zano/lib/zano_formatter.dart
Normal file
74
cw_zano/lib/zano_formatter.dart
Normal file
|
@ -0,0 +1,74 @@
|
|||
import 'dart:math';
|
||||
|
||||
import 'package:cw_zano/zano_wallet_api.dart';
|
||||
import 'package:decimal/decimal.dart';
|
||||
import 'package:decimal/intl.dart';
|
||||
import 'package:fluttertoast/fluttertoast.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
class ZanoFormatter {
|
||||
static const defaultDecimalPoint = 12;
|
||||
|
||||
//static final numberFormat = NumberFormat()
|
||||
// ..maximumFractionDigits = defaultDecimalPoint
|
||||
// ..minimumFractionDigits = 1;
|
||||
|
||||
static Decimal _bigIntDivision({required BigInt amount, required BigInt divider}) {
|
||||
return (Decimal.fromBigInt(amount) / Decimal.fromBigInt(divider)).toDecimal();
|
||||
}
|
||||
|
||||
static String intAmountToString(int amount, [int decimalPoint = defaultDecimalPoint]) {
|
||||
final numberFormat = NumberFormat()..maximumFractionDigits = decimalPoint
|
||||
..minimumFractionDigits = 1;
|
||||
return numberFormat.format(
|
||||
DecimalIntl(
|
||||
_bigIntDivision(
|
||||
amount: BigInt.from(amount),
|
||||
divider: BigInt.from(pow(10, decimalPoint)),
|
||||
),
|
||||
),
|
||||
)
|
||||
.replaceAll(',', '');
|
||||
}
|
||||
|
||||
static String bigIntAmountToString(BigInt amount, [int decimalPoint = defaultDecimalPoint]) {
|
||||
final numberFormat = NumberFormat()..maximumFractionDigits = decimalPoint
|
||||
..minimumFractionDigits = 1;
|
||||
return numberFormat.format(
|
||||
DecimalIntl(
|
||||
_bigIntDivision(
|
||||
amount: amount,
|
||||
divider: BigInt.from(pow(10, decimalPoint)),
|
||||
),
|
||||
),
|
||||
)
|
||||
.replaceAll(',', '');
|
||||
}
|
||||
|
||||
static double intAmountToDouble(int amount, [int decimalPoint = defaultDecimalPoint]) => _bigIntDivision(
|
||||
amount: BigInt.from(amount),
|
||||
divider: BigInt.from(pow(10, decimalPoint)),
|
||||
).toDouble();
|
||||
|
||||
static int parseAmount(String amount, [int decimalPoint = defaultDecimalPoint]) {
|
||||
final resultBigInt = (Decimal.parse(amount) * Decimal.fromBigInt(BigInt.from(10).pow(decimalPoint))).toBigInt();
|
||||
if (!resultBigInt.isValidInt) {
|
||||
Fluttertoast.showToast(msg: 'Cannot transfer $amount. Maximum is ${intAmountToString(resultBigInt.toInt(), decimalPoint)}.');
|
||||
}
|
||||
return resultBigInt.toInt();
|
||||
}
|
||||
|
||||
static BigInt bigIntFromDynamic(dynamic d) {
|
||||
if (d is int) {
|
||||
return BigInt.from(d);
|
||||
} else if (d is BigInt) {
|
||||
return d;
|
||||
} else if (d == null) {
|
||||
return BigInt.zero;
|
||||
} else {
|
||||
ZanoWalletApi.error('cannot cast value of type ${d.runtimeType} to BigInt');
|
||||
throw 'cannot cast value of type ${d.runtimeType} to BigInt';
|
||||
//return BigInt.zero;
|
||||
}
|
||||
}
|
||||
}
|
27
cw_zano/lib/zano_transaction_history.dart
Normal file
27
cw_zano/lib/zano_transaction_history.dart
Normal file
|
@ -0,0 +1,27 @@
|
|||
import 'dart:core';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:cw_core/transaction_history.dart';
|
||||
import 'package:cw_zano/model/zano_transaction_info.dart';
|
||||
|
||||
part 'zano_transaction_history.g.dart';
|
||||
|
||||
class ZanoTransactionHistory = ZanoTransactionHistoryBase
|
||||
with _$ZanoTransactionHistory;
|
||||
|
||||
abstract class ZanoTransactionHistoryBase
|
||||
extends TransactionHistoryBase<ZanoTransactionInfo> with Store {
|
||||
ZanoTransactionHistoryBase() {
|
||||
transactions = ObservableMap<String, ZanoTransactionInfo>();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> save() async {}
|
||||
|
||||
@override
|
||||
void addOne(ZanoTransactionInfo transaction) =>
|
||||
transactions[transaction.id] = transaction;
|
||||
|
||||
@override
|
||||
void addMany(Map<String, ZanoTransactionInfo> transactions) =>
|
||||
this.transactions.addAll(transactions);
|
||||
}
|
17
cw_zano/lib/zano_utils.dart
Normal file
17
cw_zano/lib/zano_utils.dart
Normal file
|
@ -0,0 +1,17 @@
|
|||
import 'dart:convert';
|
||||
|
||||
import 'package:monero/zano.dart' as zano;
|
||||
import 'package:cw_zano/api/model/get_address_info_result.dart';
|
||||
|
||||
class ZanoUtils {
|
||||
static bool validateAddress(String address) {
|
||||
try {
|
||||
final result = GetAddressInfoResult.fromJson(
|
||||
jsonDecode(zano.PlainWallet_getAddressInfo(address)) as Map<String, dynamic>,
|
||||
);
|
||||
return result.valid;
|
||||
} catch (err) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
545
cw_zano/lib/zano_wallet.dart
Normal file
545
cw_zano/lib/zano_wallet.dart
Normal file
|
@ -0,0 +1,545 @@
|
|||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:cw_core/cake_hive.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/node.dart';
|
||||
import 'package:cw_core/pathForWallet.dart';
|
||||
import 'package:cw_core/pending_transaction.dart';
|
||||
import 'package:cw_core/sync_status.dart';
|
||||
import 'package:cw_core/transaction_priority.dart';
|
||||
import 'package:cw_core/utils/print_verbose.dart';
|
||||
import 'package:cw_core/wallet_base.dart';
|
||||
import 'package:cw_core/wallet_credentials.dart';
|
||||
import 'package:cw_core/wallet_info.dart';
|
||||
import 'package:cw_core/zano_asset.dart';
|
||||
import 'package:cw_zano/api/model/create_wallet_result.dart';
|
||||
import 'package:cw_zano/api/model/destination.dart';
|
||||
import 'package:cw_zano/api/model/get_recent_txs_and_info_result.dart';
|
||||
import 'package:cw_zano/api/model/get_wallet_status_result.dart';
|
||||
import 'package:cw_zano/api/model/transfer.dart';
|
||||
import 'package:cw_zano/model/pending_zano_transaction.dart';
|
||||
import 'package:cw_zano/model/zano_balance.dart';
|
||||
import 'package:cw_zano/model/zano_transaction_creation_exception.dart';
|
||||
import 'package:cw_zano/model/zano_transaction_credentials.dart';
|
||||
import 'package:cw_zano/model/zano_transaction_info.dart';
|
||||
import 'package:cw_zano/model/zano_wallet_keys.dart';
|
||||
import 'package:cw_zano/zano_formatter.dart';
|
||||
import 'package:cw_zano/zano_transaction_history.dart';
|
||||
import 'package:cw_zano/zano_wallet_addresses.dart';
|
||||
import 'package:cw_zano/zano_wallet_api.dart';
|
||||
import 'package:cw_zano/zano_wallet_exceptions.dart';
|
||||
import 'package:cw_zano/zano_wallet_service.dart';
|
||||
import 'package:cw_zano/api/model/balance.dart';
|
||||
|
||||
import 'package:mobx/mobx.dart';
|
||||
|
||||
part 'zano_wallet.g.dart';
|
||||
|
||||
class ZanoWallet = ZanoWalletBase with _$ZanoWallet;
|
||||
|
||||
abstract class ZanoWalletBase
|
||||
extends WalletBase<ZanoBalance, ZanoTransactionHistory, ZanoTransactionInfo>
|
||||
with Store, ZanoWalletApi {
|
||||
static const int _autoSaveIntervalSeconds = 30;
|
||||
static const int _pollIntervalMilliseconds = 2000;
|
||||
static const int _maxLoadAssetsRetries = 5;
|
||||
|
||||
@override
|
||||
void setPassword(String password) {
|
||||
_password = password;
|
||||
super.setPassword(password);
|
||||
}
|
||||
|
||||
String _password;
|
||||
|
||||
@override
|
||||
String get password => _password;
|
||||
|
||||
@override
|
||||
Future<String> signMessage(String message, {String? address = null}) {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> verifyMessage(String message, String signature, {String? address = null}) {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
@override
|
||||
ZanoWalletAddresses walletAddresses;
|
||||
|
||||
@override
|
||||
@observable
|
||||
SyncStatus syncStatus;
|
||||
|
||||
@override
|
||||
@observable
|
||||
ObservableMap<CryptoCurrency, ZanoBalance> balance;
|
||||
|
||||
@override
|
||||
String seed = '';
|
||||
|
||||
@override
|
||||
ZanoWalletKeys keys = ZanoWalletKeys(
|
||||
privateSpendKey: '', privateViewKey: '', publicSpendKey: '', publicViewKey: '');
|
||||
|
||||
static const String zanoAssetId =
|
||||
'd6329b5b1f7c0805b5c345f4957554002a2f557845f64d7645dae0e051a6498a';
|
||||
|
||||
Map<String, ZanoAsset> zanoAssets = {};
|
||||
|
||||
Timer? _updateSyncInfoTimer;
|
||||
|
||||
int _lastKnownBlockHeight = 0;
|
||||
int _initialSyncHeight = 0;
|
||||
int currentDaemonHeight = 0;
|
||||
bool _isTransactionUpdating;
|
||||
bool _hasSyncAfterStartup;
|
||||
Timer? _autoSaveTimer;
|
||||
|
||||
/// number of transactions in each request
|
||||
static final int _txChunkSize = (pow(2, 32) - 1).toInt();
|
||||
|
||||
ZanoWalletBase(WalletInfo walletInfo, String password)
|
||||
: balance = ObservableMap.of({CryptoCurrency.zano: ZanoBalance.empty()}),
|
||||
_isTransactionUpdating = false,
|
||||
_hasSyncAfterStartup = false,
|
||||
walletAddresses = ZanoWalletAddresses(walletInfo),
|
||||
syncStatus = NotConnectedSyncStatus(),
|
||||
_password = password,
|
||||
super(walletInfo) {
|
||||
transactionHistory = ZanoTransactionHistory();
|
||||
if (!CakeHive.isAdapterRegistered(ZanoAsset.typeId)) {
|
||||
CakeHive.registerAdapter(ZanoAssetAdapter());
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
int calculateEstimatedFee(TransactionPriority priority, [int? amount = null]) =>
|
||||
getCurrentTxFee(priority);
|
||||
|
||||
@override
|
||||
Future<void> changePassword(String password) async {
|
||||
setPassword(password);
|
||||
}
|
||||
|
||||
static Future<ZanoWallet> create({required WalletCredentials credentials}) async {
|
||||
final wallet = ZanoWallet(credentials.walletInfo!, credentials.password!);
|
||||
await wallet.initWallet();
|
||||
final path = await pathForWallet(name: credentials.name, type: credentials.walletInfo!.type);
|
||||
final createWalletResult = await wallet.createWallet(path, credentials.password!);
|
||||
await wallet.initWallet();
|
||||
await wallet.parseCreateWalletResult(createWalletResult);
|
||||
await wallet.init(createWalletResult.wi.address);
|
||||
return wallet;
|
||||
}
|
||||
|
||||
static Future<ZanoWallet> restore(
|
||||
{required ZanoRestoreWalletFromSeedCredentials credentials}) async {
|
||||
final wallet = ZanoWallet(credentials.walletInfo!, credentials.password!);
|
||||
await wallet.initWallet();
|
||||
final path = await pathForWallet(name: credentials.name, type: credentials.walletInfo!.type);
|
||||
final createWalletResult = await wallet.restoreWalletFromSeed(
|
||||
path, credentials.password!, credentials.mnemonic, credentials.passphrase);
|
||||
await wallet.initWallet();
|
||||
await wallet.parseCreateWalletResult(createWalletResult);
|
||||
await wallet.init(createWalletResult.wi.address);
|
||||
return wallet;
|
||||
}
|
||||
|
||||
static Future<ZanoWallet> open(
|
||||
{required String name, required String password, required WalletInfo walletInfo}) async {
|
||||
final path = await pathForWallet(name: name, type: walletInfo.type);
|
||||
final wallet = ZanoWallet(walletInfo, password);
|
||||
await wallet.initWallet();
|
||||
final createWalletResult = await wallet.loadWallet(path, password);
|
||||
await wallet.initWallet();
|
||||
await wallet.parseCreateWalletResult(createWalletResult);
|
||||
await wallet.init(createWalletResult.wi.address);
|
||||
return wallet;
|
||||
}
|
||||
|
||||
Future<void> parseCreateWalletResult(CreateWalletResult result) async {
|
||||
hWallet = result.walletId;
|
||||
seed = result.seed;
|
||||
ZanoWalletApi.info('setting hWallet = ${result.walletId}');
|
||||
walletAddresses.address = result.wi.address;
|
||||
await loadAssets(result.wi.balances, maxRetries: _maxLoadAssetsRetries);
|
||||
for (final item in result.wi.balances) {
|
||||
if (item.assetInfo.assetId == zanoAssetId) {
|
||||
balance[CryptoCurrency.zano] = ZanoBalance(
|
||||
total: item.total,
|
||||
unlocked: item.unlocked,
|
||||
);
|
||||
}
|
||||
}
|
||||
if (result.recentHistory.history != null) {
|
||||
final transfers = result.recentHistory.history!;
|
||||
final transactions = Transfer.makeMap(transfers, zanoAssets, currentDaemonHeight);
|
||||
transactionHistory.addMany(transactions);
|
||||
await transactionHistory.save();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> close({bool shouldCleanup = true}) async {
|
||||
closeWallet();
|
||||
_updateSyncInfoTimer?.cancel();
|
||||
_autoSaveTimer?.cancel();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> connectToNode({required Node node}) async {
|
||||
syncStatus = ConnectingSyncStatus();
|
||||
await setupNode(node.uriRaw);
|
||||
syncStatus = ConnectedSyncStatus();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<PendingTransaction> createTransaction(Object credentials) async {
|
||||
credentials as ZanoTransactionCredentials;
|
||||
final isZano = credentials.currency == CryptoCurrency.zano;
|
||||
final outputs = credentials.outputs;
|
||||
final hasMultiDestination = outputs.length > 1;
|
||||
final unlockedBalanceZano = balance[CryptoCurrency.zano]?.unlocked ?? BigInt.zero;
|
||||
final unlockedBalanceCurrency = balance[credentials.currency]?.unlocked ?? BigInt.zero;
|
||||
final fee = BigInt.from(calculateEstimatedFee(credentials.priority));
|
||||
late BigInt totalAmount;
|
||||
void checkForEnoughBalances() {
|
||||
if (isZano) {
|
||||
if (totalAmount + fee > unlockedBalanceZano) {
|
||||
throw ZanoTransactionCreationException(
|
||||
"You don't have enough coins (required: ${ZanoFormatter.bigIntAmountToString(totalAmount + fee)} ZANO, unlocked ${ZanoFormatter.bigIntAmountToString(unlockedBalanceZano)} ZANO).");
|
||||
}
|
||||
} else {
|
||||
if (fee > unlockedBalanceZano) {
|
||||
throw ZanoTransactionCreationException(
|
||||
"You don't have enough coins (required: ${ZanoFormatter.bigIntAmountToString(fee)} ZANO, unlocked ${ZanoFormatter.bigIntAmountToString(unlockedBalanceZano)} ZANO).");
|
||||
}
|
||||
if (totalAmount > unlockedBalanceCurrency) {
|
||||
throw ZanoTransactionCreationException(
|
||||
"You don't have enough coins (required: ${ZanoFormatter.bigIntAmountToString(totalAmount, credentials.currency.decimals)} ${credentials.currency.title}, unlocked ${ZanoFormatter.bigIntAmountToString(unlockedBalanceCurrency, credentials.currency.decimals)} ${credentials.currency.title}).");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final assetId = isZano ? zanoAssetId : (credentials.currency as ZanoAsset).assetId;
|
||||
late List<Destination> destinations;
|
||||
if (hasMultiDestination) {
|
||||
if (outputs.any((output) => output.sendAll || (output.formattedCryptoAmount ?? 0) <= 0)) {
|
||||
throw ZanoTransactionCreationException("You don't have enough coins.");
|
||||
}
|
||||
totalAmount = outputs.fold(
|
||||
BigInt.zero, (acc, value) => acc + BigInt.from(value.formattedCryptoAmount ?? 0));
|
||||
checkForEnoughBalances();
|
||||
destinations = outputs
|
||||
.map((output) => Destination(
|
||||
amount: BigInt.from(output.formattedCryptoAmount ?? 0),
|
||||
address: output.isParsedAddress ? output.extractedAddress! : output.address,
|
||||
assetId: assetId,
|
||||
))
|
||||
.toList();
|
||||
} else {
|
||||
final output = outputs.first;
|
||||
if (output.sendAll) {
|
||||
if (isZano) {
|
||||
totalAmount = unlockedBalanceZano - fee;
|
||||
} else {
|
||||
totalAmount = unlockedBalanceCurrency;
|
||||
}
|
||||
} else {
|
||||
totalAmount = BigInt.from(output.formattedCryptoAmount!);
|
||||
}
|
||||
checkForEnoughBalances();
|
||||
destinations = [
|
||||
Destination(
|
||||
amount: totalAmount,
|
||||
address: output.isParsedAddress ? output.extractedAddress! : output.address,
|
||||
assetId: assetId,
|
||||
)
|
||||
];
|
||||
}
|
||||
return PendingZanoTransaction(
|
||||
zanoWallet: this,
|
||||
destinations: destinations,
|
||||
fee: fee,
|
||||
comment: outputs.first.note ?? '',
|
||||
assetId: assetId,
|
||||
ticker: credentials.currency.title,
|
||||
decimalPoint: credentials.currency.decimals,
|
||||
amount: totalAmount,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Map<String, ZanoTransactionInfo>> fetchTransactions() async {
|
||||
try {
|
||||
final transfers = <Transfer>[];
|
||||
late GetRecentTxsAndInfoResult result;
|
||||
do {
|
||||
result = await getRecentTxsAndInfo(offset: 0, count: _txChunkSize);
|
||||
// _lastTxIndex += result.transfers.length;
|
||||
transfers.addAll(result.transfers);
|
||||
} while (result.lastItemIndex + 1 < result.totalTransfers);
|
||||
return Transfer.makeMap(transfers, zanoAssets, currentDaemonHeight);
|
||||
} catch (e) {
|
||||
ZanoWalletApi.error(e.toString());
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> init(String address) async {
|
||||
await walletAddresses.init();
|
||||
await walletAddresses.updateAddress(address);
|
||||
await updateTransactions();
|
||||
_autoSaveTimer = Timer.periodic(Duration(seconds: _autoSaveIntervalSeconds), (_) async {
|
||||
await save();
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> renameWalletFiles(String newWalletName) async {
|
||||
final currentWalletPath = await pathForWallet(name: name, type: type);
|
||||
final currentCacheFile = File(currentWalletPath);
|
||||
final currentKeysFile = File('$currentWalletPath.keys');
|
||||
final currentAddressListFile = File('$currentWalletPath.address.txt');
|
||||
|
||||
final newWalletPath = await pathForWallet(name: newWalletName, type: type);
|
||||
|
||||
// Copies current wallet files into new wallet name's dir and files
|
||||
if (currentCacheFile.existsSync()) {
|
||||
await currentCacheFile.copy(newWalletPath);
|
||||
}
|
||||
if (currentKeysFile.existsSync()) {
|
||||
await currentKeysFile.copy('$newWalletPath.keys');
|
||||
}
|
||||
if (currentAddressListFile.existsSync()) {
|
||||
await currentAddressListFile.copy('$newWalletPath.address.txt');
|
||||
}
|
||||
|
||||
// Delete old name's dir and files
|
||||
await Directory(currentWalletPath).delete(recursive: true);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> rescan({required int height}) => throw UnimplementedError();
|
||||
|
||||
@override
|
||||
Future<void> save() async {
|
||||
try {
|
||||
await store();
|
||||
await walletAddresses.updateAddressesInBox();
|
||||
} catch (e) {
|
||||
ZanoWalletApi.error('Error while saving Zano wallet file ${e.toString()}');
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> loadAssets(List<Balance> balances, {int maxRetries = 1}) async {
|
||||
List<ZanoAsset> assets = [];
|
||||
int retryCount = 0;
|
||||
|
||||
while (retryCount < maxRetries) {
|
||||
try {
|
||||
assets = await getAssetsWhitelist();
|
||||
break;
|
||||
} on ZanoWalletBusyException {
|
||||
if (retryCount < maxRetries - 1) {
|
||||
retryCount++;
|
||||
await Future.delayed(Duration(seconds: 1));
|
||||
} else {
|
||||
ZanoWalletApi.error('failed to load assets after $retryCount retries');
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
zanoAssets = {};
|
||||
for (final asset in assets) {
|
||||
final newAsset = ZanoAsset.copyWith(
|
||||
asset,
|
||||
enabled: balances.any((element) => element.assetId == asset.assetId),
|
||||
);
|
||||
zanoAssets.putIfAbsent(asset.assetId, () => newAsset);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> startSync() async {
|
||||
try {
|
||||
syncStatus = AttemptingSyncStatus();
|
||||
_lastKnownBlockHeight = 0;
|
||||
_initialSyncHeight = 0;
|
||||
_updateSyncInfoTimer ??=
|
||||
Timer.periodic(Duration(milliseconds: _pollIntervalMilliseconds), (_) async {
|
||||
GetWalletStatusResult walletStatus;
|
||||
// ignoring get wallet status exception (in case of wrong wallet id)
|
||||
try {
|
||||
walletStatus = await getWalletStatus();
|
||||
} on ZanoWalletException {
|
||||
return;
|
||||
}
|
||||
currentDaemonHeight = walletStatus.currentDaemonHeight;
|
||||
_updateSyncProgress(walletStatus);
|
||||
|
||||
// we can call getWalletInfo ONLY if getWalletStatus returns NOT is in long refresh and wallet state is 2 (ready)
|
||||
if (!walletStatus.isInLongRefresh && walletStatus.walletState == 2) {
|
||||
final walletInfo = await getWalletInfo();
|
||||
seed = walletInfo.wiExtended.seed;
|
||||
keys = ZanoWalletKeys(
|
||||
privateSpendKey: walletInfo.wiExtended.spendPrivateKey,
|
||||
privateViewKey: walletInfo.wiExtended.viewPrivateKey,
|
||||
publicSpendKey: walletInfo.wiExtended.spendPublicKey,
|
||||
publicViewKey: walletInfo.wiExtended.viewPublicKey,
|
||||
);
|
||||
loadAssets(walletInfo.wi.balances);
|
||||
// matching balances and whitelists
|
||||
// 1. show only balances available in whitelists
|
||||
// 2. set whitelists available in balances as 'enabled' ('disabled' by default)
|
||||
for (final b in walletInfo.wi.balances) {
|
||||
if (b.assetId == zanoAssetId) {
|
||||
balance[CryptoCurrency.zano] = ZanoBalance(total: b.total, unlocked: b.unlocked);
|
||||
} else {
|
||||
final asset = zanoAssets[b.assetId];
|
||||
if (asset == null) {
|
||||
ZanoWalletApi.error('balance for an unknown asset ${b.assetInfo.assetId}');
|
||||
continue;
|
||||
}
|
||||
if (balance.keys.any(
|
||||
(element) => element is ZanoAsset && element.assetId == b.assetInfo.assetId)) {
|
||||
balance[balance.keys.firstWhere((element) =>
|
||||
element is ZanoAsset && element.assetId == b.assetInfo.assetId)] =
|
||||
ZanoBalance(
|
||||
total: b.total, unlocked: b.unlocked, decimalPoint: asset.decimalPoint);
|
||||
} else {
|
||||
balance[asset] = ZanoBalance(
|
||||
total: b.total, unlocked: b.unlocked, decimalPoint: asset.decimalPoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
await updateTransactions();
|
||||
// removing balances for assets missing in wallet info balances
|
||||
balance.removeWhere(
|
||||
(key, _) =>
|
||||
key != CryptoCurrency.zano &&
|
||||
!walletInfo.wi.balances
|
||||
.any((element) => element.assetId == (key as ZanoAsset).assetId),
|
||||
);
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
syncStatus = FailedSyncStatus();
|
||||
ZanoWalletApi.error(e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void>? updateBalance() => null;
|
||||
|
||||
Future<void> updateTransactions() async {
|
||||
try {
|
||||
if (_isTransactionUpdating) {
|
||||
return;
|
||||
}
|
||||
_isTransactionUpdating = true;
|
||||
final transactions = await fetchTransactions();
|
||||
if (transactions.length == transactionHistory.transactions.length) {
|
||||
_isTransactionUpdating = false;
|
||||
return;
|
||||
}
|
||||
transactionHistory.clear();
|
||||
transactionHistory.addMany(transactions);
|
||||
await transactionHistory.save();
|
||||
_isTransactionUpdating = false;
|
||||
} catch (e) {
|
||||
printV("e: $e");
|
||||
ZanoWalletApi.error(e.toString());
|
||||
_isTransactionUpdating = false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<CryptoCurrency> addZanoAssetById(String assetId) async {
|
||||
if (zanoAssets.containsKey(assetId)) {
|
||||
throw ZanoWalletException('zano asset with id $assetId already added');
|
||||
}
|
||||
final assetDescriptor = await addAssetsWhitelist(assetId);
|
||||
if (assetDescriptor == null) {
|
||||
throw ZanoWalletException("there's no zano asset with id $assetId");
|
||||
}
|
||||
final asset = ZanoAsset.copyWith(
|
||||
assetDescriptor,
|
||||
assetId: assetId,
|
||||
enabled: true,
|
||||
);
|
||||
zanoAssets[asset.assetId] = asset;
|
||||
balance[asset] = ZanoBalance.empty(decimalPoint: asset.decimalPoint);
|
||||
return asset;
|
||||
}
|
||||
|
||||
Future<void> changeZanoAssetAvailability(ZanoAsset asset) async {
|
||||
if (asset.enabled) {
|
||||
final assetDescriptor = await addAssetsWhitelist(asset.assetId);
|
||||
if (assetDescriptor == null) {
|
||||
ZanoWalletApi.error('Error adding zano asset');
|
||||
}
|
||||
} else {
|
||||
final result = await removeAssetsWhitelist(asset.assetId);
|
||||
if (result == false) {
|
||||
ZanoWalletApi.error('Error removing zano asset');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> deleteZanoAsset(ZanoAsset asset) async {
|
||||
final _ = await removeAssetsWhitelist(asset.assetId);
|
||||
}
|
||||
|
||||
Future<ZanoAsset?> getZanoAsset(String assetId) async {
|
||||
return await getAssetInfo(assetId);
|
||||
}
|
||||
|
||||
Future<void> _askForUpdateTransactionHistory() async => await updateTransactions();
|
||||
|
||||
void _onNewBlock(int height, int blocksLeft, double ptc) async {
|
||||
try {
|
||||
if (blocksLeft < 1000) {
|
||||
await _askForUpdateTransactionHistory();
|
||||
syncStatus = SyncedSyncStatus();
|
||||
|
||||
if (!_hasSyncAfterStartup) {
|
||||
_hasSyncAfterStartup = true;
|
||||
await save();
|
||||
}
|
||||
} else {
|
||||
syncStatus = SyncingSyncStatus(blocksLeft, ptc);
|
||||
}
|
||||
} catch (e) {
|
||||
ZanoWalletApi.error(e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
void _updateSyncProgress(GetWalletStatusResult walletStatus) {
|
||||
final syncHeight = walletStatus.currentWalletHeight;
|
||||
if (_initialSyncHeight <= 0) {
|
||||
_initialSyncHeight = syncHeight;
|
||||
}
|
||||
final bchHeight = walletStatus.currentDaemonHeight;
|
||||
|
||||
if (_lastKnownBlockHeight == syncHeight) {
|
||||
return;
|
||||
}
|
||||
|
||||
_lastKnownBlockHeight = syncHeight;
|
||||
final track = bchHeight - _initialSyncHeight;
|
||||
final diff = track - (bchHeight - syncHeight);
|
||||
final ptc = diff <= 0 ? 0.0 : diff / track;
|
||||
final left = bchHeight - syncHeight;
|
||||
|
||||
if (syncHeight < 0 || left < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 1. Actual new height; 2. Blocks left to finish; 3. Progress in percents;
|
||||
_onNewBlock.call(syncHeight, left, ptc);
|
||||
}
|
||||
}
|
40
cw_zano/lib/zano_wallet_addresses.dart
Normal file
40
cw_zano/lib/zano_wallet_addresses.dart
Normal file
|
@ -0,0 +1,40 @@
|
|||
import 'package:cw_core/wallet_addresses.dart';
|
||||
import 'package:cw_core/wallet_info.dart';
|
||||
import 'package:cw_zano/zano_wallet_api.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
|
||||
part 'zano_wallet_addresses.g.dart';
|
||||
|
||||
class ZanoWalletAddresses = ZanoWalletAddressesBase with _$ZanoWalletAddresses;
|
||||
|
||||
abstract class ZanoWalletAddressesBase extends WalletAddresses with Store {
|
||||
ZanoWalletAddressesBase(WalletInfo walletInfo)
|
||||
: address = '',
|
||||
super(walletInfo);
|
||||
|
||||
@override
|
||||
@observable
|
||||
String address;
|
||||
|
||||
@override
|
||||
Future<void> init() async {
|
||||
address = walletInfo.address;
|
||||
await updateAddressesInBox();
|
||||
}
|
||||
|
||||
Future<void> updateAddress(String address) async {
|
||||
this.address = address;
|
||||
await updateAddressesInBox();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> updateAddressesInBox() async {
|
||||
try {
|
||||
addressesMap.clear();
|
||||
addressesMap[address] = '';
|
||||
await saveAddressesInBox();
|
||||
} catch (e) {
|
||||
ZanoWalletApi.error(e.toString());
|
||||
}
|
||||
}
|
||||
}
|
467
cw_zano/lib/zano_wallet_api.dart
Normal file
467
cw_zano/lib/zano_wallet_api.dart
Normal file
|
@ -0,0 +1,467 @@
|
|||
import 'dart:convert' as convert;
|
||||
import 'dart:ffi';
|
||||
import 'dart:isolate';
|
||||
|
||||
import 'package:cw_core/transaction_priority.dart';
|
||||
import 'package:cw_core/utils/print_verbose.dart';
|
||||
import 'package:cw_core/zano_asset.dart';
|
||||
import 'package:cw_zano/api/consts.dart';
|
||||
import 'package:cw_zano/api/model/asset_id_params.dart';
|
||||
import 'package:cw_zano/api/model/create_wallet_result.dart';
|
||||
import 'package:cw_zano/api/model/destination.dart';
|
||||
import 'package:cw_zano/api/model/get_address_info_result.dart';
|
||||
import 'package:cw_zano/api/model/get_recent_txs_and_info_params.dart';
|
||||
import 'package:cw_zano/api/model/get_recent_txs_and_info_result.dart';
|
||||
import 'package:cw_zano/api/model/get_wallet_info_result.dart';
|
||||
import 'package:cw_zano/api/model/get_wallet_status_result.dart';
|
||||
import 'package:cw_zano/api/model/proxy_to_daemon_params.dart';
|
||||
import 'package:cw_zano/api/model/proxy_to_daemon_result.dart';
|
||||
import 'package:cw_zano/api/model/store_result.dart';
|
||||
import 'package:cw_zano/api/model/transfer.dart';
|
||||
import 'package:cw_zano/api/model/transfer_params.dart';
|
||||
import 'package:cw_zano/api/model/transfer_result.dart';
|
||||
import 'package:cw_zano/zano_wallet_exceptions.dart';
|
||||
import 'package:ffi/ffi.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:json_bigint/json_bigint.dart';
|
||||
import 'package:monero/zano.dart' as zano;
|
||||
import 'package:monero/src/generated_bindings_zano.g.dart' as zanoapi;
|
||||
|
||||
mixin ZanoWalletApi {
|
||||
static const _maxReopenAttempts = 5;
|
||||
static const _logInfo = false;
|
||||
static const _logError = true;
|
||||
static const _logJson = false;
|
||||
static const int _zanoMixinValue = 10;
|
||||
|
||||
int _hWallet = 0;
|
||||
|
||||
int get hWallet => _hWallet;
|
||||
|
||||
set hWallet(int value) {
|
||||
_hWallet = value;
|
||||
}
|
||||
|
||||
int getCurrentTxFee(TransactionPriority priority) => zano.PlainWallet_getCurrentTxFee(priority.raw);
|
||||
|
||||
void setPassword(String password) => zano.PlainWallet_resetWalletPassword(hWallet, password);
|
||||
|
||||
void closeWallet([int? walletToClose]) async {
|
||||
info('close_wallet ${walletToClose ?? hWallet}');
|
||||
final result = await _closeWallet(walletToClose ?? hWallet);
|
||||
info('close_wallet result $result');
|
||||
}
|
||||
|
||||
Future<bool> initWallet() async {
|
||||
// pathForWallet(name: , type: type)
|
||||
final result = zano.PlainWallet_init("", "", 0);
|
||||
printV(result);
|
||||
return result == "OK";
|
||||
}
|
||||
|
||||
Future<bool> setupNode(String nodeUrl) async {
|
||||
await _setupNode(hWallet, nodeUrl);
|
||||
return true;
|
||||
}
|
||||
|
||||
Future<GetWalletInfoResult> getWalletInfo() async {
|
||||
final json = await _getWalletInfo(hWallet);
|
||||
final result = GetWalletInfoResult.fromJson(jsonDecode(json));
|
||||
_json('get_wallet_info', json);
|
||||
info('get_wallet_info got ${result.wi.balances.length} balances: ${result.wi.balances} seed: ${_shorten(result.wiExtended.seed)}');
|
||||
return result;
|
||||
}
|
||||
|
||||
Future<GetWalletStatusResult> getWalletStatus() async {
|
||||
final json = await _getWalletStatus(hWallet);
|
||||
if (json == Consts.errorWalletWrongId) {
|
||||
error('wrong wallet id');
|
||||
throw ZanoWalletException('Wrong wallet id');
|
||||
}
|
||||
final status = GetWalletStatusResult.fromJson(jsonDecode(json));
|
||||
_json('get_wallet_status', json);
|
||||
if (_logInfo)
|
||||
info(
|
||||
'get_wallet_status connected: ${status.isDaemonConnected} in refresh: ${status.isInLongRefresh} progress: ${status.progress} wallet state: ${status.walletState} sync: ${status.currentWalletHeight}/${status.currentDaemonHeight} ${(status.currentWalletHeight/status.currentDaemonHeight*100).toStringAsFixed(2)}%');
|
||||
return status;
|
||||
}
|
||||
|
||||
Future<String> invokeMethod(String methodName, Object params) async {
|
||||
final request = jsonEncode({
|
||||
"method": methodName,
|
||||
"params": params,
|
||||
});
|
||||
final invokeResult = await callSyncMethod('invoke', hWallet, request);
|
||||
try {
|
||||
jsonDecode(invokeResult);
|
||||
} catch (e) {
|
||||
if (invokeResult.contains(Consts.errorWalletWrongId)) throw ZanoWalletException('Wrong wallet id');
|
||||
error('exception in parsing json in invokeMethod: $invokeResult');
|
||||
rethrow;
|
||||
}
|
||||
return invokeResult;
|
||||
}
|
||||
|
||||
Future<List<ZanoAsset>> getAssetsWhitelist() async {
|
||||
try {
|
||||
final json = await invokeMethod('assets_whitelist_get', '{}');
|
||||
_json('assets_whitelist_get', json);
|
||||
final map = jsonDecode(json) as Map<String, dynamic>?;
|
||||
_checkForErrors(map);
|
||||
List<ZanoAsset> assets(String type, bool isGlobalWhitelist) =>
|
||||
(map?['result']?[type] as List<dynamic>?)
|
||||
?.map((e) => ZanoAsset.fromJson(e as Map<String, dynamic>, isInGlobalWhitelist: isGlobalWhitelist))
|
||||
.toList() ??
|
||||
[];
|
||||
final localWhitelist = assets('local_whitelist', false);
|
||||
final globalWhitelist = assets('global_whitelist', true);
|
||||
final ownAssets = assets('own_assets', false);
|
||||
if (_logInfo)
|
||||
info('assets_whitelist_get got local whitelist: ${localWhitelist.length} ($localWhitelist); '
|
||||
'global whitelist: ${globalWhitelist.length} ($globalWhitelist); '
|
||||
'own assets: ${ownAssets.length} ($ownAssets)');
|
||||
return [...globalWhitelist, ...localWhitelist, ...ownAssets];
|
||||
} catch (e) {
|
||||
error('assets_whitelist_get $e');
|
||||
return [];
|
||||
// rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
Future<ZanoAsset?> addAssetsWhitelist(String assetId) async {
|
||||
try {
|
||||
final json = await invokeMethod('assets_whitelist_add', AssetIdParams(assetId: assetId));
|
||||
_json('assets_whitelist_add $assetId', json);
|
||||
final map = jsonDecode(json) as Map<String, dynamic>?;
|
||||
_checkForErrors(map);
|
||||
if (map!['result']!['status']! == 'OK') {
|
||||
final assetDescriptor = ZanoAsset.fromJson(map['result']!['asset_descriptor']! as Map<String, dynamic>);
|
||||
info('assets_whitelist_add added ${assetDescriptor.fullName} ${assetDescriptor.ticker}');
|
||||
return assetDescriptor;
|
||||
} else {
|
||||
info('assets_whitelist_add status ${map['result']!['status']!}');
|
||||
return null;
|
||||
}
|
||||
} catch (e) {
|
||||
error('assets_whitelist_add $e');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> removeAssetsWhitelist(String assetId) async {
|
||||
try {
|
||||
final json = await invokeMethod('assets_whitelist_remove', AssetIdParams(assetId: assetId));
|
||||
_json('assets_whitelist_remove $assetId', json);
|
||||
final map = jsonDecode(json) as Map<String, dynamic>?;
|
||||
_checkForErrors(map);
|
||||
info('assets_whitelist_remove status ${map!['result']!['status']!}');
|
||||
return (map['result']!['status']! == 'OK');
|
||||
} catch (e) {
|
||||
error('assets_whitelist_remove $e');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<ProxyToDaemonResult?> _proxyToDaemon(String uri, String body) async {
|
||||
final json = await invokeMethod('proxy_to_daemon', ProxyToDaemonParams(body: body, uri: uri));
|
||||
final map = jsonDecode(json) as Map<String, dynamic>?;
|
||||
_checkForErrors(map);
|
||||
return ProxyToDaemonResult.fromJson(map!['result'] as Map<String, dynamic>);
|
||||
}
|
||||
|
||||
Future<ZanoAsset?> getAssetInfo(String assetId) async {
|
||||
final methodName = 'get_asset_info';
|
||||
final params = AssetIdParams(assetId: assetId);
|
||||
final result = await _proxyToDaemon('/json_rpc', '{"method": "$methodName","params": ${jsonEncode(params)}}');
|
||||
_json('$methodName $assetId', result?.body ?? '');
|
||||
if (result == null) {
|
||||
error('get_asset_info empty result');
|
||||
return null;
|
||||
}
|
||||
final map = jsonDecode(result.body) as Map<String, dynamic>?;
|
||||
if (map!['error'] != null) {
|
||||
info('get_asset_info $assetId error ${map['error']!['code']} ${map['error']!['message']}');
|
||||
return null;
|
||||
} else if (map['result']!['status']! == 'OK') {
|
||||
final assetDescriptor = ZanoAsset.fromJson(map['result']!['asset_descriptor']! as Map<String, dynamic>);
|
||||
info('get_asset_info $assetId ${assetDescriptor.fullName} ${assetDescriptor.ticker}');
|
||||
return assetDescriptor;
|
||||
} else {
|
||||
info('get_asset_info $assetId status ${map['result']!['status']!}');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Future<StoreResult?> store() async {
|
||||
try {
|
||||
final json = await invokeMethod('store', '{}');
|
||||
final map = jsonDecode(json) as Map<String, dynamic>?;
|
||||
_checkForErrors(map);
|
||||
return StoreResult.fromJson(map!['result'] as Map<String, dynamic>);
|
||||
} catch (e) {
|
||||
error('store $e');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Future<GetRecentTxsAndInfoResult> getRecentTxsAndInfo({required int offset, required int count}) async {
|
||||
info('get_recent_txs_and_info $offset $count');
|
||||
try {
|
||||
final json = await invokeMethod('get_recent_txs_and_info', GetRecentTxsAndInfoParams(offset: offset, count: count));
|
||||
_json('get_recent_txs_and_info', json);
|
||||
final map = jsonDecode(json) as Map<String, dynamic>?;
|
||||
_checkForErrors(map);
|
||||
final lastItemIndex = map?['result']?['last_item_index'] as int?;
|
||||
final totalTransfers = map?['result']?['total_transfers'] as int?;
|
||||
final transfers = map?['result']?['transfers'] as List<dynamic>?;
|
||||
if (transfers == null || lastItemIndex == null || totalTransfers == null) {
|
||||
error('get_recent_txs_and_info empty transfers');
|
||||
return GetRecentTxsAndInfoResult.empty();
|
||||
}
|
||||
info('get_recent_txs_and_info transfers.length: ${transfers.length}');
|
||||
return GetRecentTxsAndInfoResult(
|
||||
transfers: transfers.map((e) => Transfer.fromJson(e as Map<String, dynamic>)).toList(),
|
||||
lastItemIndex: lastItemIndex,
|
||||
totalTransfers: totalTransfers,
|
||||
);
|
||||
} catch (e) {
|
||||
error('get_recent_txs_and_info $e');
|
||||
return GetRecentTxsAndInfoResult.empty();
|
||||
}
|
||||
}
|
||||
|
||||
GetAddressInfoResult getAddressInfo(String address) => GetAddressInfoResult.fromJson(
|
||||
jsonDecode(zano.PlainWallet_getAddressInfo(address)),
|
||||
);
|
||||
|
||||
String _shorten(String s) => s.length > 10 ? '${s.substring(0, 4)}...${s.substring(s.length - 4)}' : s;
|
||||
|
||||
Future<CreateWalletResult> createWallet(String path, String password) async {
|
||||
info('create_wallet path $path password ${_shorten(password)}');
|
||||
final json = zano.PlainWallet_generate(path, password);
|
||||
_json('create_wallet', json);
|
||||
final map = jsonDecode(json) as Map<String, dynamic>?;
|
||||
if (map?['error'] != null) {
|
||||
final code = map!['error']?['code'] ?? '';
|
||||
final message = map['error']?['message'] ?? '';
|
||||
throw ZanoWalletException('Error creating wallet file, $message ($code)');
|
||||
}
|
||||
if (map?['result'] == null) {
|
||||
throw ZanoWalletException('Error creating wallet file, empty response');
|
||||
}
|
||||
final result = CreateWalletResult.fromJson(map!['result'] as Map<String, dynamic>);
|
||||
info('create_wallet ${result.name} ${result.seed}');
|
||||
return result;
|
||||
}
|
||||
|
||||
Future<CreateWalletResult> restoreWalletFromSeed(String path, String password, String seed, String? passphrase) async {
|
||||
info('restore_wallet path $path password ${_shorten(password)} seed ${_shorten(seed)}');
|
||||
final json = zano.PlainWallet_restore(seed, path, password, passphrase??'');
|
||||
_json('restore_wallet', json);
|
||||
final map = jsonDecode(json) as Map<String, dynamic>?;
|
||||
if (map?['error'] != null) {
|
||||
final code = map!['error']!['code'] ?? '';
|
||||
final message = map['error']!['message'] ?? '';
|
||||
if (code == Consts.errorWrongSeed) {
|
||||
throw RestoreFromSeedsException('Error restoring wallet, wrong seed');
|
||||
} else if (code == Consts.errorAlreadyExists) {
|
||||
throw RestoreFromSeedsException('Error restoring wallet, already exists');
|
||||
}
|
||||
throw RestoreFromSeedsException('Error restoring wallet, $message ($code)');
|
||||
}
|
||||
if (map?['result'] == null) {
|
||||
throw RestoreFromSeedsException('Error restoring wallet, empty response');
|
||||
}
|
||||
final result = CreateWalletResult.fromJson(map!['result'] as Map<String, dynamic>);
|
||||
info('restore_wallet ${result.name} ${result.wi.address}');
|
||||
return result;
|
||||
}
|
||||
|
||||
Future<CreateWalletResult>loadWallet(String path, String password, [int attempt = 0]) async {
|
||||
info('load_wallet1 path $path password ${_shorten(password)}');
|
||||
final String json;
|
||||
try {
|
||||
json = zano.PlainWallet_open(path, password);
|
||||
} catch (e) {
|
||||
error('error in loadingWallet $e');
|
||||
rethrow;
|
||||
}
|
||||
info('load_wallet2: $json');
|
||||
final map = jsonDecode(json) as Map<String, dynamic>?;
|
||||
if (map?['error'] != null) {
|
||||
final code = map?['error']!['code'] ?? '';
|
||||
final message = map?['error']!['message'] ?? '';
|
||||
if (code == Consts.errorAlreadyExists && attempt <= _maxReopenAttempts) {
|
||||
// already connected to this wallet. closing and trying to reopen
|
||||
info('already connected. closing and reopen wallet (attempt $attempt)');
|
||||
closeWallet(attempt);
|
||||
await Future.delayed(const Duration(milliseconds: 500));
|
||||
return await loadWallet(path, password, attempt + 1);
|
||||
}
|
||||
throw ZanoWalletException('Error loading wallet, $message ($code)');
|
||||
}
|
||||
if (map?['result'] == null) {
|
||||
throw ZanoWalletException('Error loading wallet, empty response');
|
||||
}
|
||||
final result = CreateWalletResult.fromJson(map!['result'] as Map<String, dynamic>);
|
||||
info('load_wallet3 ${result.name} ${result.wi.address}');
|
||||
return result;
|
||||
}
|
||||
|
||||
Future<TransferResult> transfer(List<Destination> destinations, BigInt fee, String comment) async {
|
||||
final params = TransferParams(
|
||||
destinations: destinations,
|
||||
fee: fee,
|
||||
mixin: _zanoMixinValue,
|
||||
paymentId: '',
|
||||
comment: comment,
|
||||
pushPayer: false,
|
||||
hideReceiver: true,
|
||||
);
|
||||
final json = await invokeMethod('transfer', params);
|
||||
_json('transfer', json);
|
||||
final map = jsonDecode(json);
|
||||
final resultMap = map as Map<String, dynamic>?;
|
||||
if (resultMap != null) {
|
||||
final transferResultMap = resultMap['result'] as Map<String, dynamic>?;
|
||||
if (transferResultMap != null) {
|
||||
final transferResult = TransferResult.fromJson(transferResultMap);
|
||||
info('transfer success hash ${transferResult.txHash}');
|
||||
return transferResult;
|
||||
} else {
|
||||
final errorCode = resultMap['error']?['code'];
|
||||
final code = errorCode is int ? errorCode.toString() : errorCode as String? ?? '';
|
||||
final message = resultMap['error']?['message'] as String? ?? '';
|
||||
error('transfer error $code $message');
|
||||
throw TransferException('Transfer error, $message ($code)');
|
||||
}
|
||||
}
|
||||
error('transfer error empty result');
|
||||
throw TransferException('Transfer error, empty result');
|
||||
}
|
||||
|
||||
void _checkForErrors(Map<String, dynamic>? map) {
|
||||
if (map == null) {
|
||||
throw ZanoWalletException('Empty response');
|
||||
}
|
||||
final result = map['result'];
|
||||
if (result == null) {
|
||||
throw ZanoWalletException('Empty response');
|
||||
}
|
||||
if (result['error'] != null) {
|
||||
final code = result['error']!['code'] ?? '';
|
||||
final message = result['error']!['message'] ?? '';
|
||||
if (code == -1 && message == Consts.errorBusy) {
|
||||
throw ZanoWalletBusyException();
|
||||
}
|
||||
throw ZanoWalletException('Error, $message ($code)');
|
||||
}
|
||||
}
|
||||
|
||||
/*Future<void> _writeLog(String method, String logMessage) async {
|
||||
final dir = await getDownloadsDirectory();
|
||||
final logFile = File('${dir!.path}/$method.txt');
|
||||
final date = DateTime.now();
|
||||
String twoDigits(int value) => value.toString().padLeft(2, '0');
|
||||
String removeCRandLF(String input) => input.replaceAll(RegExp('\r|\n'), '');
|
||||
await logFile.writeAsString('${twoDigits(date.hour)}:${twoDigits(date.minute)}:${twoDigits(date.second)} ${removeCRandLF(logMessage)}\n',
|
||||
mode: FileMode.append);
|
||||
}*/
|
||||
|
||||
static void info(String s) => _logInfo ? debugPrint('[info] $s') : null;
|
||||
static void error(String s) => _logError ? debugPrint('[error] $s') : null;
|
||||
static void printWrapped(String text) => RegExp('.{1,800}').allMatches(text).map((m) => m.group(0)).forEach(print);
|
||||
static void _json(String methodName, String json) => _logJson ? printWrapped('$methodName $json') : null;
|
||||
|
||||
}
|
||||
|
||||
Future<String> callSyncMethod(String methodName, int hWallet, String params) async {
|
||||
final params_ = params.toNativeUtf8().address;
|
||||
final method_name_ = methodName.toNativeUtf8().address;
|
||||
final invokeResult = await Isolate.run(() async {
|
||||
final lib = zanoapi.ZanoC(DynamicLibrary.open(zano.libPath));
|
||||
final txid = lib.ZANO_PlainWallet_syncCall(
|
||||
Pointer.fromAddress(method_name_).cast(),
|
||||
hWallet,
|
||||
Pointer.fromAddress(params_).cast()
|
||||
);
|
||||
try {
|
||||
final strPtr = txid.cast<Utf8>();
|
||||
final str = strPtr.toDartString();
|
||||
lib.ZANO_free(strPtr.cast());
|
||||
return str;
|
||||
} catch (e) {
|
||||
return "";
|
||||
}
|
||||
});
|
||||
calloc.free(Pointer.fromAddress(method_name_));
|
||||
calloc.free(Pointer.fromAddress(params_));
|
||||
return invokeResult;
|
||||
}
|
||||
|
||||
Map<String, dynamic> jsonDecode(String json) {
|
||||
try {
|
||||
return decodeJson(json.replaceAll("\\/", "/")) as Map<String, dynamic>;
|
||||
} catch (e) {
|
||||
return convert.jsonDecode(json) as Map<String, dynamic>;
|
||||
}
|
||||
}
|
||||
|
||||
String jsonEncode(Object? object) {
|
||||
return convert.jsonEncode(object);
|
||||
}
|
||||
|
||||
Future<String> _getWalletStatus(int hWallet) async {
|
||||
final jsonPtr = await Isolate.run(() async {
|
||||
final lib = zanoapi.ZanoC(DynamicLibrary.open(zano.libPath));
|
||||
final status = lib.ZANO_PlainWallet_getWalletStatus(
|
||||
hWallet,
|
||||
);
|
||||
return status.address;
|
||||
});
|
||||
String json = "";
|
||||
try {
|
||||
final strPtr = Pointer.fromAddress(jsonPtr).cast<Utf8>();
|
||||
final str = strPtr.toDartString();
|
||||
zano.ZANO_free(strPtr.cast());
|
||||
json = str;
|
||||
} catch (e) {
|
||||
json = "";
|
||||
}
|
||||
return json;
|
||||
}
|
||||
Future<String> _getWalletInfo(int hWallet) async {
|
||||
final jsonPtr = await Isolate.run(() async {
|
||||
final lib = zanoapi.ZanoC(DynamicLibrary.open(zano.libPath));
|
||||
final status = lib.ZANO_PlainWallet_getWalletInfo(
|
||||
hWallet,
|
||||
);
|
||||
return status.address;
|
||||
});
|
||||
String json = "";
|
||||
try {
|
||||
final strPtr = Pointer.fromAddress(jsonPtr).cast<Utf8>();
|
||||
final str = strPtr.toDartString();
|
||||
zano.ZANO_free(strPtr.cast());
|
||||
json = str;
|
||||
} catch (e) {
|
||||
json = "";
|
||||
}
|
||||
return json;
|
||||
}
|
||||
|
||||
Future<String> _setupNode(int hWallet, String nodeUrl) async {
|
||||
final resp = await callSyncMethod("reset_connection_url", hWallet, nodeUrl);
|
||||
printV(resp);
|
||||
final resp2 = await callSyncMethod("run_wallet", hWallet, "");
|
||||
printV(resp2);
|
||||
return "OK";
|
||||
}
|
||||
|
||||
Future<String> _closeWallet(int hWallet) async {
|
||||
final str = await Isolate.run(() async {
|
||||
return zano.PlainWallet_closeWallet(hWallet);
|
||||
});
|
||||
printV("Closing wallet: $str");
|
||||
return str;
|
||||
}
|
19
cw_zano/lib/zano_wallet_exceptions.dart
Normal file
19
cw_zano/lib/zano_wallet_exceptions.dart
Normal file
|
@ -0,0 +1,19 @@
|
|||
class ZanoWalletException implements Exception {
|
||||
final String message;
|
||||
|
||||
ZanoWalletException(this.message);
|
||||
@override
|
||||
String toString() => '${this.runtimeType} (message: $message)';
|
||||
}
|
||||
|
||||
class RestoreFromSeedsException extends ZanoWalletException {
|
||||
RestoreFromSeedsException(String message) : super(message);
|
||||
}
|
||||
|
||||
class TransferException extends ZanoWalletException {
|
||||
TransferException(String message): super(message);
|
||||
}
|
||||
|
||||
class ZanoWalletBusyException extends ZanoWalletException {
|
||||
ZanoWalletBusyException(): super('');
|
||||
}
|
123
cw_zano/lib/zano_wallet_service.dart
Normal file
123
cw_zano/lib/zano_wallet_service.dart
Normal file
|
@ -0,0 +1,123 @@
|
|||
import 'dart:io';
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:cw_core/pathForWallet.dart';
|
||||
import 'package:cw_core/wallet_base.dart';
|
||||
import 'package:cw_core/wallet_credentials.dart';
|
||||
import 'package:cw_core/wallet_info.dart';
|
||||
import 'package:cw_core/wallet_service.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:cw_zano/zano_wallet.dart';
|
||||
import 'package:cw_zano/zano_wallet_api.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:monero/zano.dart' as zano;
|
||||
|
||||
class ZanoNewWalletCredentials extends WalletCredentials {
|
||||
ZanoNewWalletCredentials({required String name, String? password}) : super(name: name, password: password);
|
||||
}
|
||||
|
||||
class ZanoRestoreWalletFromSeedCredentials extends WalletCredentials {
|
||||
ZanoRestoreWalletFromSeedCredentials({required String name, required String password, required String passphrase, required int height, required this.mnemonic})
|
||||
: super(name: name, password: password, passphrase: passphrase, height: height);
|
||||
|
||||
final String mnemonic;
|
||||
}
|
||||
|
||||
class ZanoRestoreWalletFromKeysCredentials extends WalletCredentials {
|
||||
ZanoRestoreWalletFromKeysCredentials(
|
||||
{required String name,
|
||||
required String password,
|
||||
required this.language,
|
||||
required this.address,
|
||||
required this.viewKey,
|
||||
required this.spendKey,
|
||||
required int height})
|
||||
: super(name: name, password: password, height: height);
|
||||
|
||||
final String language;
|
||||
final String address;
|
||||
final String viewKey;
|
||||
final String spendKey;
|
||||
}
|
||||
|
||||
class ZanoWalletService extends WalletService<ZanoNewWalletCredentials,
|
||||
ZanoRestoreWalletFromSeedCredentials, ZanoRestoreWalletFromKeysCredentials, ZanoNewWalletCredentials> {
|
||||
ZanoWalletService(this.walletInfoSource);
|
||||
|
||||
final Box<WalletInfo> walletInfoSource;
|
||||
|
||||
static bool walletFilesExist(String path) => !File(path).existsSync() && !File('$path.keys').existsSync();
|
||||
|
||||
int hWallet = 0;
|
||||
|
||||
@override
|
||||
WalletType getType() => WalletType.zano;
|
||||
|
||||
@override
|
||||
Future<ZanoWallet> create(WalletCredentials credentials, {bool? isTestnet}) async {
|
||||
ZanoWalletApi.info('zanowallet service create isTestnet $isTestnet');
|
||||
return await ZanoWalletBase.create(credentials: credentials);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> isWalletExit(String name) async {
|
||||
final path = await pathForWallet(name: name, type: getType());
|
||||
return zano.PlainWallet_isWalletExist(path);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<ZanoWallet> openWallet(String name, String password) async {
|
||||
final walletInfo = walletInfoSource.values.firstWhereOrNull((info) => info.id == WalletBase.idFor(name, getType()))!;
|
||||
try {
|
||||
final wallet = await ZanoWalletBase.open(name: name, password: password, walletInfo: walletInfo);
|
||||
saveBackup(name);
|
||||
return wallet;
|
||||
} catch (e) {
|
||||
await restoreWalletFilesFromBackup(name);
|
||||
return await ZanoWalletBase.open(name: name, password: password, walletInfo: walletInfo);
|
||||
}
|
||||
}
|
||||
|
||||
@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()));
|
||||
final currentWallet = ZanoWallet(currentWalletInfo, password);
|
||||
|
||||
await currentWallet.renameWalletFiles(newName);
|
||||
|
||||
final newWalletInfo = currentWalletInfo;
|
||||
newWalletInfo.id = WalletBase.idFor(newName, getType());
|
||||
newWalletInfo.name = newName;
|
||||
|
||||
await walletInfoSource.put(currentWalletInfo.key, newWalletInfo);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<ZanoWallet> restoreFromKeys(ZanoRestoreWalletFromKeysCredentials credentials, {bool? isTestnet}) async {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<ZanoWallet> restoreFromSeed(ZanoRestoreWalletFromSeedCredentials credentials, {bool? isTestnet}) async {
|
||||
return ZanoWalletBase.restore(credentials: credentials);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<ZanoWallet> restoreFromHardwareWallet(ZanoNewWalletCredentials credentials) {
|
||||
throw UnimplementedError("Restoring a Zano wallet from a hardware wallet is not yet supported!");
|
||||
}
|
||||
}
|
827
cw_zano/pubspec.lock
Normal file
827
cw_zano/pubspec.lock
Normal file
|
@ -0,0 +1,827 @@
|
|||
# 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: bf9f5caeea8d8fe6721a9c358dd8a5c1947b27f1cfaa18b39c301273594919e6
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.6.0"
|
||||
asn1lib:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: asn1lib
|
||||
sha256: "4bae5ae63e6d6dd17c4aac8086f3dec26c0236f6a0f03416c6c19d830c367cf5"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.5.8"
|
||||
async:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: async
|
||||
sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.11.0"
|
||||
boolean_selector:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: boolean_selector
|
||||
sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.1"
|
||||
build:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: build
|
||||
sha256: "80184af8b6cb3e5c1c4ec6d8544d27711700bc3e6d2efad04238c7b5290889f0"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.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: "79b2aef6ac2ed00046867ed354c88778c9c0f029df8a20fe10b5436826721ef9"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.0.2"
|
||||
build_resolvers:
|
||||
dependency: "direct dev"
|
||||
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: "028819cfb90051c6b5440c7e574d1896f8037e3c96cf17aaeb054c9311cfbf4d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.13"
|
||||
build_runner_core:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: build_runner_core
|
||||
sha256: "6d6ee4276b1c5f34f21fdf39425202712d2be82019983d52f351c94aafbc2c41"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.2.10"
|
||||
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: c7913a9737ee4007efedaffc968c049fd0f3d0e49109e778edc10de9426005cb
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "8.9.2"
|
||||
cake_backup:
|
||||
dependency: transitive
|
||||
description:
|
||||
path: "."
|
||||
ref: main
|
||||
resolved-ref: "3aba867dcab6737f6707782f5db15d71f303db38"
|
||||
url: "https://github.com/cake-tech/cake_backup.git"
|
||||
source: git
|
||||
version: "1.0.0+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: "0ec10bf4a89e4c613960bf1e8b42c64127021740fb21640c29c909826a5eea3e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.10.1"
|
||||
collection:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: collection
|
||||
sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.18.0"
|
||||
convert:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: convert
|
||||
sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.2"
|
||||
crypto:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: crypto
|
||||
sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.6"
|
||||
cryptography:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: cryptography
|
||||
sha256: d146b76d33d94548cf035233fbc2f4338c1242fa119013bead807d033fc4ae05
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.7.0"
|
||||
cupertino_icons:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: cupertino_icons
|
||||
sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.8"
|
||||
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"
|
||||
encrypt:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: encrypt
|
||||
sha256: "62d9aa4670cc2a8798bab89b39fc71b6dfbacf615de6cf5001fb39f7e4a996a2"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.0.3"
|
||||
fake_async:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: fake_async
|
||||
sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.1"
|
||||
ffi:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: ffi
|
||||
sha256: "16ed7b077ef01ad6170a3d0c57caa4a112a38d7a2ed5602e0aca9ca6f3d98da6"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.3"
|
||||
file:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: file
|
||||
sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.0.1"
|
||||
fixnum:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: fixnum
|
||||
sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.1"
|
||||
flutter:
|
||||
dependency: "direct main"
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_mobx:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_mobx
|
||||
sha256: "859fbf452fa9c2519d2700b125dd7fb14c508bbdd7fb65e26ca8ff6c92280e2e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.1+1"
|
||||
flutter_test:
|
||||
dependency: "direct dev"
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_web_plugins:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
fluttertoast:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: fluttertoast
|
||||
sha256: "95f349437aeebe524ef7d6c9bde3e6b4772717cf46a0eb6a3ceaddc740b297cc"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "8.2.8"
|
||||
frontend_server_client:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: frontend_server_client
|
||||
sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.0.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: "741bbf84165310a68ff28fe9e727332eef1407342fca52759cb21ad8177bb8d0"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.2"
|
||||
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: b9c29a161230ee03d3ccf545097fccd9b87a5264228c5d348202e0f0c28f9010
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.2"
|
||||
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: "direct main"
|
||||
description:
|
||||
name: intl
|
||||
sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.19.0"
|
||||
io:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: io
|
||||
sha256: dfd5a80599cf0165756e3181807ed3e77daf6dd4137caaad72d0b7931597650b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.5"
|
||||
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: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.9.0"
|
||||
json_bigint:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: json_bigint
|
||||
sha256: "9e613e731847ab2154d67160682adf104cbd9863741ec2f7abfcf6e77c70592f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.0"
|
||||
leak_tracker:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker
|
||||
sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "10.0.5"
|
||||
leak_tracker_flutter_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker_flutter_testing
|
||||
sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.5"
|
||||
leak_tracker_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker_testing
|
||||
sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.1"
|
||||
logging:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: logging
|
||||
sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.0"
|
||||
matcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: matcher
|
||||
sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.12.16+1"
|
||||
material_color_utilities:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: material_color_utilities
|
||||
sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.11.1"
|
||||
meta:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: meta
|
||||
sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.15.0"
|
||||
mime:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: mime
|
||||
sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
mobx:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: mobx
|
||||
sha256: "1f01a429529ac55e5e80c0fcad62c60112fb91df3dec11a9113d71cf0c2e2c4c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.0"
|
||||
mobx_codegen:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: mobx_codegen
|
||||
sha256: d4beb9cea4b7b014321235f8fdc7c2193ee0fe1d1198e9da7403f8bc85c4407c
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.0"
|
||||
monero:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "impls/monero.dart"
|
||||
ref: "9526921acb774b523a2e1d9ba9a7b389acfc6b70"
|
||||
resolved-ref: "9526921acb774b523a2e1d9ba9a7b389acfc6b70"
|
||||
url: "https://github.com/mrcyjanek/monero_c"
|
||||
source: git
|
||||
version: "0.0.0"
|
||||
nested:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: nested
|
||||
sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
package_config:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: package_config
|
||||
sha256: "92d4488434b520a62570293fbd33bb556c7d49230791c1b4bbd973baf6d2dc67"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.1"
|
||||
path:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path
|
||||
sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.9.0"
|
||||
path_provider:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: path_provider
|
||||
sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.5"
|
||||
path_provider_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_android
|
||||
sha256: "4adf4fd5423ec60a29506c76581bc05854c55e3a0b72d35bb28d661c9686edf2"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.15"
|
||||
path_provider_foundation:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_foundation
|
||||
sha256: "4843174df4d288f5e29185bd6e72a6fbdf5a4a4602717eed565497429f179942"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.1"
|
||||
path_provider_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_linux
|
||||
sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.1"
|
||||
path_provider_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_platform_interface
|
||||
sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.2"
|
||||
path_provider_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_windows
|
||||
sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.0"
|
||||
petitparser:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: petitparser
|
||||
sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.0.2"
|
||||
platform:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: platform
|
||||
sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.6"
|
||||
plugin_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: plugin_platform_interface
|
||||
sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.8"
|
||||
pointycastle:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: pointycastle
|
||||
sha256: "4be0097fcf3fd3e8449e53730c631200ebc7b88016acecab2b0da2f0149222fe"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.9.1"
|
||||
pool:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: pool
|
||||
sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.5.1"
|
||||
provider:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: provider
|
||||
sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.1.2"
|
||||
pub_semver:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: pub_semver
|
||||
sha256: "7b3cfbf654f3edd0c6298ecd5be782ce997ddf0e00531b9464b55245185bbbbd"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.5"
|
||||
pubspec_parse:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: pubspec_parse
|
||||
sha256: c799b721d79eb6ee6fa56f00c04b472dcd44a30d258fac2174a6ec57302678f8
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.0"
|
||||
rational:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: rational
|
||||
sha256: cb808fb6f1a839e6fc5f7d8cb3b0a10e1db48b3be102de73938c627f0b636336
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.3"
|
||||
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: cc36c297b52866d203dbf9332263c94becc2fe0ceaa9681d07b6ef9807023b67
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.1"
|
||||
sky_engine:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.99"
|
||||
socks5_proxy:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: socks5_proxy
|
||||
sha256: "616818a0ea1064a4823b53c9f7eaf8da64ed82dcd51ed71371c7e54751ed5053"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.6"
|
||||
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: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.10.0"
|
||||
stack_trace:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stack_trace
|
||||
sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.11.1"
|
||||
stream_channel:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stream_channel
|
||||
sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.2"
|
||||
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: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.2"
|
||||
timing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: timing
|
||||
sha256: "62ee18aca144e4a9f29d212f5a4c6a053be252b895ab14b5821996cff4ed90fe"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.2"
|
||||
tuple:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: tuple
|
||||
sha256: a97ce2013f240b2f3807bcbaf218765b6f301c3eff91092bcfa23a039e7dd151
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.2"
|
||||
typed_data:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: typed_data
|
||||
sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.0"
|
||||
unorm_dart:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: unorm_dart
|
||||
sha256: "23d8bf65605401a6a32cff99435fed66ef3dab3ddcad3454059165df46496a3b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.3.0"
|
||||
vector_math:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: vector_math
|
||||
sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.4"
|
||||
vm_service:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: vm_service
|
||||
sha256: f652077d0bdf60abe4c1f6377448e8655008eef28f128bc023f7b5e8dfeb48fc
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "14.2.4"
|
||||
watcher:
|
||||
dependency: "direct overridden"
|
||||
description:
|
||||
name: watcher
|
||||
sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: web
|
||||
sha256: cd3543bd5798f6ad290ea73d210f423502e71900302dde696f8bff84bf89a1cb
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
web_socket:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: web_socket
|
||||
sha256: "3c12d96c0c9a4eec095246debcea7b86c0324f22df69893d538fcc6f1b8cce83"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.1.6"
|
||||
web_socket_channel:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: web_socket_channel
|
||||
sha256: "9f187088ed104edd8662ca07af4b124465893caf063ba29758f97af57e61da8f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.1"
|
||||
xdg_directories:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: xdg_directories
|
||||
sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
yaml:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: yaml
|
||||
sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.2"
|
||||
sdks:
|
||||
dart: ">=3.5.0 <4.0.0"
|
||||
flutter: ">=3.24.0"
|
81
cw_zano/pubspec.yaml
Normal file
81
cw_zano/pubspec.yaml
Normal file
|
@ -0,0 +1,81 @@
|
|||
name: cw_zano
|
||||
description: A new flutter plugin project.
|
||||
version: 0.0.1
|
||||
publish_to: none
|
||||
author: Cake Wallet
|
||||
homepage: https://cakewallet.com
|
||||
|
||||
environment:
|
||||
sdk: ">=2.19.0 <3.0.0"
|
||||
flutter: ">=1.20.0"
|
||||
|
||||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
ffi: ^2.0.1
|
||||
http: ^1.1.0
|
||||
path_provider: ^2.0.11
|
||||
mobx: ^2.1.4
|
||||
flutter_mobx: ^2.0.6+1
|
||||
intl: ^0.19.0
|
||||
decimal: ^2.3.3
|
||||
cw_core:
|
||||
path: ../cw_core
|
||||
json_bigint: ^3.0.0
|
||||
fluttertoast: ^8.2.8
|
||||
monero:
|
||||
git:
|
||||
url: https://github.com/mrcyjanek/monero_c
|
||||
ref: 9526921acb774b523a2e1d9ba9a7b389acfc6b70 # monero_c hash
|
||||
path: impls/monero.dart
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
build_runner: ^2.4.7
|
||||
mobx_codegen: ^2.1.1
|
||||
build_resolvers: ^2.0.9
|
||||
hive_generator: ^1.1.3
|
||||
|
||||
dependency_overrides:
|
||||
watcher: ^1.1.0
|
||||
|
||||
|
||||
# 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.
|
||||
flutter:
|
||||
# This section identifies this Flutter project as a plugin project.
|
||||
# The 'pluginClass' and Android 'package' identifiers should not ordinarily
|
||||
# be modified. They are used by the tooling to maintain consistency when
|
||||
# adding or updating assets for this project.
|
||||
# To add assets to your plugin 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 plugin 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
|
2
ios/.gitignore
vendored
2
ios/.gitignore
vendored
|
@ -31,4 +31,4 @@ Runner/GeneratedPluginRegistrant.*
|
|||
!default.pbxuser
|
||||
!default.perspectivev3
|
||||
|
||||
Mwebd.xcframework
|
||||
Mwebd.xcframework
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
A3D5E17CC53DF13FA740DEFA /* RedeemSwap.swift in Resources */ = {isa = PBXBuildFile; fileRef = 9D2F2C9F2555316C95EE7EA3 /* RedeemSwap.swift */; settings = {ASSET_TAGS = (BreezSDK, ); }; };
|
||||
B6C6E59403ACDE44724C12F4 /* ServiceConfig.swift in Resources */ = {isa = PBXBuildFile; fileRef = B3D5E78267F5F18D882FDC3B /* ServiceConfig.swift */; settings = {ASSET_TAGS = (BreezSDK, ); }; };
|
||||
CE291CFE2C15DB9A00B9F709 /* WowneroWallet.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = CE291CFD2C15DB9A00B9F709 /* WowneroWallet.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
CEA883682D43BE4500278CD3 /* ZanoWallet.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = CEB6D62E2D43BB78002C6DBC /* ZanoWallet.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
CEAFE4A02C53926F009FF3AD /* libresolv.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = C58D93382C00FAC6004BCF69 /* libresolv.tbd */; };
|
||||
CFEFC24F82F78FE747DF1D22 /* LnurlPayInfo.swift in Resources */ = {isa = PBXBuildFile; fileRef = 58C22CBD8C22B9D6023D59F8 /* LnurlPayInfo.swift */; settings = {ASSET_TAGS = (BreezSDK, ); }; };
|
||||
D0D7A0D4E13F31C4E02E235B /* ReceivePayment.swift in Resources */ = {isa = PBXBuildFile; fileRef = 91C524F800843E0A3F17E004 /* ReceivePayment.swift */; settings = {ASSET_TAGS = (BreezSDK, ); }; };
|
||||
|
@ -44,6 +45,7 @@
|
|||
files = (
|
||||
CE291CFE2C15DB9A00B9F709 /* WowneroWallet.framework in CopyFiles */,
|
||||
0C50DFB92BF3CB56002B0EB3 /* MoneroWallet.framework in CopyFiles */,
|
||||
CEA883682D43BE4500278CD3 /* ZanoWallet.framework in CopyFiles */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
@ -86,6 +88,7 @@
|
|||
C58D93382C00FAC6004BCF69 /* libresolv.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libresolv.tbd; path = usr/lib/libresolv.tbd; sourceTree = SDKROOT; };
|
||||
CE291CFD2C15DB9A00B9F709 /* WowneroWallet.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = WowneroWallet.framework; sourceTree = "<group>"; };
|
||||
CEAFE49D2C539250009FF3AD /* Mwebd.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; path = Mwebd.xcframework; sourceTree = "<group>"; };
|
||||
CEB6D62E2D43BB78002C6DBC /* ZanoWallet.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = ZanoWallet.framework; sourceTree = "<group>"; };
|
||||
D139E30AEB36740C21C00A9E /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
|
||||
D7CD6B6020744E8FA471915D /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
DCEA540E3586164FB47AD13E /* LnurlPayInvoice.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = LnurlPayInvoice.swift; path = "../.symlinks/plugins/breez_sdk/ios/bindings-swift/Sources/BreezSDK/Task/LnurlPayInvoice.swift"; sourceTree = "<group>"; };
|
||||
|
@ -161,6 +164,7 @@
|
|||
children = (
|
||||
CE291CFD2C15DB9A00B9F709 /* WowneroWallet.framework */,
|
||||
0C50DFB82BF3CB56002B0EB3 /* MoneroWallet.framework */,
|
||||
CEB6D62E2D43BB78002C6DBC /* ZanoWallet.framework */,
|
||||
0C44A7182518EF4A00B570ED /* CakeWallet */,
|
||||
9740EEB11CF90186004384FC /* Flutter */,
|
||||
97C146F01CF9000F007C117D /* Runner */,
|
||||
|
@ -495,6 +499,7 @@
|
|||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/Flutter",
|
||||
"$(PROJECT_DIR)",
|
||||
);
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
|
||||
|
@ -641,6 +646,7 @@
|
|||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/Flutter",
|
||||
"$(PROJECT_DIR)",
|
||||
);
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
|
||||
|
@ -679,6 +685,7 @@
|
|||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/Flutter",
|
||||
"$(PROJECT_DIR)",
|
||||
);
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
|
||||
|
|
BIN
ios/ZanoWallet.framework/Info.plist
Normal file
BIN
ios/ZanoWallet.framework/Info.plist
Normal file
Binary file not shown.
Binary file not shown.
|
@ -1 +1 @@
|
|||
../scripts/monero_c/release/monero/host-apple-ios_libwallet2_api_c.dylib
|
||||
../scripts/monero_c/release/monero/aarch64-apple-ios_libwallet2_api_c.dylib
|
|
@ -1 +1 @@
|
|||
../scripts/monero_c/release/wownero/host-apple-ios_libwallet2_api_c.dylib
|
||||
../scripts/monero_c/release/wownero/aarch64-apple-ios_libwallet2_api_c.dylib
|
|
@ -1 +1 @@
|
|||
../scripts/monero_c/release/zano/host-apple-ios_libwallet2_api_c.dylib
|
||||
../scripts/monero_c/release/zano/aarch64-apple-ios_libwallet2_api_c.dylib
|
|
@ -2,9 +2,9 @@ import 'package:bitcoin_base/bitcoin_base.dart';
|
|||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/core/validator.dart';
|
||||
import 'package:cake_wallet/solana/solana.dart';
|
||||
import 'package:cake_wallet/zano/zano.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/erc20_token.dart';
|
||||
|
||||
const BEFORE_REGEX = '(^|\\s)';
|
||||
const AFTER_REGEX = '(\$|\\s)';
|
||||
|
||||
|
@ -19,7 +19,9 @@ class AddressValidator extends TextValidator {
|
|||
? BitcoinNetwork.mainnet
|
||||
: LitecoinNetwork.mainnet,
|
||||
)
|
||||
: null,
|
||||
: type == CryptoCurrency.zano
|
||||
? zano?.validateAddress
|
||||
: null,
|
||||
pattern: getPattern(type),
|
||||
length: getLength(type));
|
||||
|
||||
|
@ -132,6 +134,8 @@ class AddressValidator extends TextValidator {
|
|||
pattern = 'D([1-9a-km-zA-HJ-NP-Z]){33}';
|
||||
case CryptoCurrency.btcln:
|
||||
pattern = '(lnbc|LNBC)([0-9]{1,}[a-zA-Z0-9]+)';
|
||||
case CryptoCurrency.zano:
|
||||
pattern = r'([1-9A-HJ-NP-Za-km-z]{90,200})|(@[\w\d-.]+)';
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
|
@ -271,6 +275,7 @@ class AddressValidator extends TextValidator {
|
|||
return [64];
|
||||
case CryptoCurrency.btcln:
|
||||
case CryptoCurrency.kaspa:
|
||||
case CryptoCurrency.zano:
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
@ -310,6 +315,8 @@ class AddressValidator extends TextValidator {
|
|||
pattern = '[1-9A-HJ-NP-Za-km-z]+';
|
||||
case CryptoCurrency.trx:
|
||||
pattern = '(T|t)[1-9A-HJ-NP-Za-km-z]{33}';
|
||||
case CryptoCurrency.zano:
|
||||
pattern = '([1-9A-HJ-NP-Za-km-z]{90,200})|(@[\w\d-.]+)';
|
||||
default:
|
||||
if (type.tag == CryptoCurrency.eth.title) {
|
||||
pattern = '0x[0-9a-zA-Z]{42}';
|
||||
|
|
|
@ -76,6 +76,8 @@ class DecimalAmountValidator extends TextValidator {
|
|||
return '^([0-9]+([.\,][0-9]{1,12})?|[.\,][0-9]{1,12})\$';
|
||||
case CryptoCurrency.btc:
|
||||
return '^([0-9]+([.\,][0-9]{1,8})?|[.\,][0-9]{1,8})\$';
|
||||
case CryptoCurrency.zano:
|
||||
return '^([0-9]+([.\,][0-9]{1,12})?|[.\,][0-9]{1,18})\$';
|
||||
default:
|
||||
return '^([0-9]+([.\,][0-9]{1,12})?|[.\,][0-9]{1,12})\$';
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import 'package:cake_wallet/polygon/polygon.dart';
|
|||
import 'package:cake_wallet/solana/solana.dart';
|
||||
import 'package:cake_wallet/tron/tron.dart';
|
||||
import 'package:cake_wallet/wownero/wownero.dart';
|
||||
import 'package:cake_wallet/zano/zano.dart';
|
||||
import 'package:cake_wallet/utils/language_list.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
|
||||
|
@ -21,7 +22,8 @@ class SeedValidator extends Validator<MnemonicItem> {
|
|||
final String language;
|
||||
final List<String> _words;
|
||||
|
||||
static List<String> getWordList({required WalletType type, required String language}) {
|
||||
static List<String> getWordList(
|
||||
{required WalletType type, required String language}) {
|
||||
switch (type) {
|
||||
case WalletType.bitcoin:
|
||||
return getBitcoinWordList(language);
|
||||
|
@ -46,6 +48,8 @@ class SeedValidator extends Validator<MnemonicItem> {
|
|||
return tron!.getTronWordList(language);
|
||||
case WalletType.wownero:
|
||||
return wownero!.getWowneroWordList(language);
|
||||
case WalletType.zano:
|
||||
return zano!.getWordList(language);
|
||||
case WalletType.none:
|
||||
return [];
|
||||
}
|
||||
|
|
|
@ -89,6 +89,7 @@ class WalletCreationService {
|
|||
case WalletType.haven:
|
||||
case WalletType.nano:
|
||||
case WalletType.banano:
|
||||
case WalletType.zano:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -245,6 +245,7 @@ import 'package:cake_wallet/view_model/wallet_seed_view_model.dart';
|
|||
import 'package:cake_wallet/view_model/wallet_unlock_loadable_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_unlock_verifiable_view_model.dart';
|
||||
import 'package:cake_wallet/wownero/wownero.dart';
|
||||
import 'package:cake_wallet/zano/zano.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/wallet_info.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
|
@ -1087,6 +1088,8 @@ Future<void> setup({
|
|||
return tron!.createTronWalletService(_walletInfoSource, SettingsStoreBase.walletPasswordDirectInput);
|
||||
case WalletType.wownero:
|
||||
return wownero!.createWowneroWalletService(_walletInfoSource, _unspentCoinsInfoSource);
|
||||
case WalletType.zano:
|
||||
return zano!.createZanoWalletService(_walletInfoSource);
|
||||
case WalletType.none:
|
||||
throw Exception('Unexpected token: ${param1.toString()} for generating of WalletService');
|
||||
}
|
||||
|
@ -1415,4 +1418,4 @@ Future<void> setup({
|
|||
getIt.registerFactory(() => SeedVerificationPage(getIt.get<WalletSeedViewModel>()));
|
||||
|
||||
_isSetupFinished = true;
|
||||
}
|
||||
}
|
|
@ -44,6 +44,7 @@ const solanaDefaultNodeUri = 'solana-mainnet.core.chainstack.com';
|
|||
const tronDefaultNodeUri = 'api.trongrid.io';
|
||||
const newCakeWalletBitcoinUri = 'btc-electrum.cakewallet.com:50002';
|
||||
const wowneroDefaultNodeUri = 'node3.monerodevs.org:34568';
|
||||
const zanoDefaultNodeUri = '37.27.100.59:10500';
|
||||
const moneroWorldNodeUri = '.moneroworld.com';
|
||||
|
||||
Future<void> defaultSettingsMigration(
|
||||
|
@ -260,7 +261,11 @@ Future<void> defaultSettingsMigration(
|
|||
await removeMoneroWorld(sharedPreferences: sharedPreferences, nodes: nodes);
|
||||
break;
|
||||
case 41:
|
||||
_deselectExchangeProvider(sharedPreferences, "Quantex");
|
||||
_changeExchangeProviderAvailability(
|
||||
sharedPreferences,
|
||||
providerName: "Quantex",
|
||||
enabled: false,
|
||||
);
|
||||
await _addSethNode(nodes, sharedPreferences);
|
||||
await updateTronNodesWithNowNodes(sharedPreferences: sharedPreferences, nodes: nodes);
|
||||
break;
|
||||
|
@ -269,8 +274,16 @@ Future<void> defaultSettingsMigration(
|
|||
break;
|
||||
case 43:
|
||||
_fixNodesUseSSLFlag(nodes);
|
||||
_deselectExchangeProvider(sharedPreferences, "THORChain");
|
||||
_deselectExchangeProvider(sharedPreferences, "SimpleSwap");
|
||||
_changeExchangeProviderAvailability(
|
||||
sharedPreferences,
|
||||
providerName: "THORChain",
|
||||
enabled: false,
|
||||
);
|
||||
_changeExchangeProviderAvailability(
|
||||
sharedPreferences,
|
||||
providerName: "SimpleSwap",
|
||||
enabled: false,
|
||||
);
|
||||
break;
|
||||
case 44:
|
||||
_fixNodesUseSSLFlag(nodes);
|
||||
|
@ -311,6 +324,7 @@ Future<void> defaultSettingsMigration(
|
|||
type: WalletType.ethereum,
|
||||
useSSL: true,
|
||||
);
|
||||
|
||||
_changeDefaultNode(
|
||||
nodes: nodes,
|
||||
sharedPreferences: sharedPreferences,
|
||||
|
@ -373,6 +387,15 @@ Future<void> defaultSettingsMigration(
|
|||
useSSL: true,
|
||||
);
|
||||
break;
|
||||
case 47:
|
||||
await addZanoNodeList(nodes: nodes);
|
||||
await changeZanoCurrentNodeToDefault(sharedPreferences: sharedPreferences, nodes: nodes);
|
||||
_changeExchangeProviderAvailability(
|
||||
sharedPreferences,
|
||||
providerName: "SimpleSwap",
|
||||
enabled: true,
|
||||
);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -469,12 +492,13 @@ Future<void> updateWalletTypeNodesWithNewNode({
|
|||
);
|
||||
}
|
||||
|
||||
void _deselectExchangeProvider(SharedPreferences sharedPreferences, String providerName) {
|
||||
void _changeExchangeProviderAvailability(SharedPreferences sharedPreferences,
|
||||
{required String providerName, required bool enabled}) {
|
||||
final Map<String, dynamic> exchangeProvidersSelection =
|
||||
json.decode(sharedPreferences.getString(PreferencesKey.exchangeProvidersSelection) ?? "{}")
|
||||
as Map<String, dynamic>;
|
||||
|
||||
exchangeProvidersSelection[providerName] = false;
|
||||
exchangeProvidersSelection[providerName] = enabled;
|
||||
|
||||
sharedPreferences.setString(
|
||||
PreferencesKey.exchangeProvidersSelection,
|
||||
|
@ -703,6 +727,12 @@ Node? getBitcoinCashDefaultElectrumServer({required Box<Node> nodes}) {
|
|||
nodes.values.firstWhereOrNull((node) => node.type == WalletType.bitcoinCash);
|
||||
}
|
||||
|
||||
Node? getZanoDefaultNode({required Box<Node> nodes}) {
|
||||
return nodes.values.firstWhereOrNull(
|
||||
(Node node) => node.uriRaw == zanoDefaultNodeUri)
|
||||
?? nodes.values.firstWhereOrNull((node) => node.type == WalletType.zano);
|
||||
}
|
||||
|
||||
Node getMoneroDefaultNode({required Box<Node> nodes}) {
|
||||
var nodeUri = newCakeWalletMoneroUri;
|
||||
|
||||
|
@ -1199,6 +1229,7 @@ Future<void> checkCurrentNodes(
|
|||
final currentSolanaNodeId = sharedPreferences.getInt(PreferencesKey.currentSolanaNodeIdKey);
|
||||
final currentTronNodeId = sharedPreferences.getInt(PreferencesKey.currentTronNodeIdKey);
|
||||
final currentWowneroNodeId = sharedPreferences.getInt(PreferencesKey.currentWowneroNodeIdKey);
|
||||
final currentZanoNodeId = sharedPreferences.getInt(PreferencesKey.currentZanoNodeIdKey);
|
||||
final currentMoneroNode =
|
||||
nodeSource.values.firstWhereOrNull((node) => node.key == currentMoneroNodeId);
|
||||
final currentBitcoinElectrumServer =
|
||||
|
@ -1223,6 +1254,8 @@ Future<void> checkCurrentNodes(
|
|||
nodeSource.values.firstWhereOrNull((node) => node.key == currentTronNodeId);
|
||||
final currentWowneroNodeServer =
|
||||
nodeSource.values.firstWhereOrNull((node) => node.key == currentWowneroNodeId);
|
||||
final currentZanoNode = nodeSource.values.firstWhereOrNull((node) => node.key == currentZanoNodeId);
|
||||
|
||||
if (currentMoneroNode == null) {
|
||||
final newCakeWalletNode = Node(uri: newCakeWalletMoneroUri, type: WalletType.monero);
|
||||
await nodeSource.add(newCakeWalletNode);
|
||||
|
@ -1306,6 +1339,12 @@ Future<void> checkCurrentNodes(
|
|||
await nodeSource.add(node);
|
||||
await sharedPreferences.setInt(PreferencesKey.currentWowneroNodeIdKey, node.key as int);
|
||||
}
|
||||
|
||||
if (currentZanoNode == null) {
|
||||
final node = Node(uri: zanoDefaultNodeUri, type: WalletType.zano);
|
||||
await nodeSource.add(node);
|
||||
await sharedPreferences.setInt(PreferencesKey.currentZanoNodeIdKey, node.key as int);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> resetBitcoinElectrumServer(
|
||||
|
@ -1381,6 +1420,15 @@ Future<void> addWowneroNodeList({required Box<Node> nodes}) async {
|
|||
}
|
||||
}
|
||||
|
||||
Future<void> addZanoNodeList({required Box<Node> nodes}) async {
|
||||
final nodeList = await loadDefaultZanoNodes();
|
||||
for (var node in nodeList) {
|
||||
if (nodes.values.firstWhereOrNull((element) => element.uriRaw == node.uriRaw) == null) {
|
||||
await nodes.add(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> changeWowneroCurrentNodeToDefault(
|
||||
{required SharedPreferences sharedPreferences, required Box<Node> nodes}) async {
|
||||
final node = getWowneroDefaultNode(nodes: nodes);
|
||||
|
@ -1389,6 +1437,13 @@ Future<void> changeWowneroCurrentNodeToDefault(
|
|||
await sharedPreferences.setInt(PreferencesKey.currentWowneroNodeIdKey, nodeId);
|
||||
}
|
||||
|
||||
Future<void> changeZanoCurrentNodeToDefault(
|
||||
{required SharedPreferences sharedPreferences, required Box<Node> nodes}) async {
|
||||
final node = getZanoDefaultNode(nodes: nodes);
|
||||
final nodeId = node?.key as int? ?? 0;
|
||||
await sharedPreferences.setInt(PreferencesKey.currentZanoNodeIdKey, nodeId);
|
||||
}
|
||||
|
||||
Future<void> addNanoNodeList({required Box<Node> nodes}) async {
|
||||
final nodeList = await loadDefaultNanoNodes();
|
||||
for (var node in nodeList) {
|
||||
|
|
|
@ -20,7 +20,7 @@ class EnsRecord {
|
|||
}
|
||||
|
||||
if (_client == null) {
|
||||
_client = Web3Client("https://ethereum.publicnode.com", Client());
|
||||
_client = Web3Client("https://ethereum-rpc.publicnode.com", Client());
|
||||
}
|
||||
|
||||
try {
|
||||
|
|
|
@ -200,6 +200,23 @@ Future<List<Node>> loadDefaultWowneroNodes() async {
|
|||
return nodes;
|
||||
}
|
||||
|
||||
Future<List<Node>> loadDefaultZanoNodes() async {
|
||||
final nodesRaw = await rootBundle.loadString('assets/zano_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.zano;
|
||||
nodes.add(node);
|
||||
}
|
||||
}
|
||||
|
||||
return nodes;
|
||||
}
|
||||
|
||||
Future<void> resetToDefault(Box<Node> nodeSource) async {
|
||||
final moneroNodes = await loadDefaultNodes();
|
||||
final bitcoinElectrumServerList = await loadBitcoinElectrumServerList();
|
||||
|
@ -211,6 +228,7 @@ Future<void> resetToDefault(Box<Node> nodeSource) async {
|
|||
final polygonNodes = await loadDefaultPolygonNodes();
|
||||
final solanaNodes = await loadDefaultSolanaNodes();
|
||||
final tronNodes = await loadDefaultTronNodes();
|
||||
final zanoNodes = await loadDefaultZanoNodes();
|
||||
|
||||
final nodes = moneroNodes +
|
||||
bitcoinElectrumServerList +
|
||||
|
@ -220,7 +238,7 @@ Future<void> resetToDefault(Box<Node> nodeSource) async {
|
|||
bitcoinCashElectrumServerList +
|
||||
nanoNodes +
|
||||
polygonNodes +
|
||||
solanaNodes + tronNodes;
|
||||
solanaNodes + tronNodes + zanoNodes;
|
||||
|
||||
await nodeSource.clear();
|
||||
await nodeSource.addAll(nodes);
|
||||
|
|
|
@ -6,6 +6,7 @@ import 'package:cake_wallet/entities/parsed_address.dart';
|
|||
import 'package:cake_wallet/entities/unstoppable_domain_address.dart';
|
||||
import 'package:cake_wallet/entities/emoji_string_extension.dart';
|
||||
import 'package:cake_wallet/entities/wellknown_record.dart';
|
||||
import 'package:cake_wallet/entities/zano_alias.dart';
|
||||
import 'package:cake_wallet/exchange/provider/thorchain_exchange.provider.dart';
|
||||
import 'package:cake_wallet/mastodon/mastodon_api.dart';
|
||||
import 'package:cake_wallet/nostr/nostr_api.dart';
|
||||
|
@ -137,6 +138,16 @@ class AddressResolver {
|
|||
try {
|
||||
// twitter handle example: @username
|
||||
if (text.startsWith('@') && !text.substring(1).contains('@')) {
|
||||
if (currency == CryptoCurrency.zano && settingsStore.lookupsZanoAlias) {
|
||||
final formattedName = text.substring(1);
|
||||
final zanoAddress = await ZanoAlias.fetchZanoAliasAddress(formattedName);
|
||||
if (zanoAddress != null) {
|
||||
return ParsedAddress.zanoAddress(
|
||||
address: zanoAddress,
|
||||
name: text,
|
||||
);
|
||||
}
|
||||
}
|
||||
if (settingsStore.lookupsTwitter) {
|
||||
final formattedName = text.substring(1);
|
||||
final twitterUser = await TwitterApi.lookupUserByName(userName: formattedName);
|
||||
|
|
|
@ -13,7 +13,8 @@ enum ParseFrom {
|
|||
mastodon,
|
||||
nostr,
|
||||
thorChain,
|
||||
wellKnown
|
||||
wellKnown,
|
||||
zanoAlias,
|
||||
}
|
||||
|
||||
class ParsedAddress {
|
||||
|
@ -143,6 +144,14 @@ class ParsedAddress {
|
|||
);
|
||||
}
|
||||
|
||||
factory ParsedAddress.zanoAddress({required String address, required String name}) {
|
||||
return ParsedAddress(
|
||||
addresses: [address],
|
||||
name: name,
|
||||
parseFrom: ParseFrom.zanoAlias,
|
||||
);
|
||||
}
|
||||
|
||||
factory ParsedAddress.fetchWellKnownAddress({required String address, required String name}) {
|
||||
return ParsedAddress(
|
||||
addresses: [address],
|
||||
|
|
|
@ -5,6 +5,7 @@ class PreferencesKey {
|
|||
static const currentBitcoinElectrumSererIdKey = 'current_node_id_btc';
|
||||
static const currentLitecoinElectrumSererIdKey = 'current_node_id_ltc';
|
||||
static const currentHavenNodeIdKey = 'current_node_id_xhv';
|
||||
static const currentZanoNodeIdKey = 'current_node_id_zano';
|
||||
static const currentEthereumNodeIdKey = 'current_node_id_eth';
|
||||
static const currentPolygonNodeIdKey = 'current_node_id_matic';
|
||||
static const currentNanoNodeIdKey = 'current_node_id_nano';
|
||||
|
@ -45,6 +46,7 @@ class PreferencesKey {
|
|||
static const ethereumTransactionPriority = 'current_fee_priority_ethereum';
|
||||
static const polygonTransactionPriority = 'current_fee_priority_polygon';
|
||||
static const bitcoinCashTransactionPriority = 'current_fee_priority_bitcoin_cash';
|
||||
static const zanoTransactionPriority = 'current_fee_priority_zano';
|
||||
static const wowneroTransactionPriority = 'current_fee_priority_wownero';
|
||||
static const customBitcoinFeeRate = 'custom_electrum_fee_rate';
|
||||
static const silentPaymentsCardDisplay = 'silentPaymentsCardDisplay';
|
||||
|
@ -71,6 +73,7 @@ class PreferencesKey {
|
|||
static const defaultNanoRep = 'default_nano_representative';
|
||||
static const defaultBananoRep = 'default_banano_representative';
|
||||
static const lookupsTwitter = 'looks_up_twitter';
|
||||
static const lookupsZanoAlias = 'looks_up_zano_alias';
|
||||
static const lookupsMastodon = 'looks_up_mastodon';
|
||||
static const lookupsYatService = 'looks_up_yat';
|
||||
static const lookupsUnstoppableDomains = 'looks_up_unstoppable_domain';
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue