From 7bc6967fc944fe9150b019a435afb063161f4ed6 Mon Sep 17 00:00:00 2001 From: Czarek Nakamoto Date: Wed, 10 Apr 2024 14:27:10 +0200 Subject: [PATCH] initial monero.dart implementation --- Makefile | 120 ++++++ android/app/src/main/AndroidManifestBase.xml | 3 +- .../app/src/main/jniLibs/arm64-v8a/.gitkeep | 0 .../app/src/main/jniLibs/armeabi-v7a/.gitkeep | 0 android/app/src/main/jniLibs/x86/.gitkeep | 0 android/app/src/main/jniLibs/x86_64/.gitkeep | 0 cw_haven/lib/api/account_list.dart | 6 +- cw_monero/example/pubspec.lock | 13 +- cw_monero/lib/api/account_list.dart | 59 +-- cw_monero/lib/api/coins_info.dart | 40 +- cw_monero/lib/api/convert_utf8_to_string.dart | 8 - cw_monero/lib/api/monero_api.dart | 6 - cw_monero/lib/api/signatures.dart | 153 -------- cw_monero/lib/api/subaddress_list.dart | 62 +--- cw_monero/lib/api/transaction_history.dart | 341 ++++++++++-------- cw_monero/lib/api/wallet.dart | 294 ++++----------- cw_monero/lib/api/wallet_manager.dart | 204 ++++------- cw_monero/lib/monero_account_list.dart | 11 +- cw_monero/lib/monero_subaddress_list.dart | 42 ++- cw_monero/lib/monero_wallet.dart | 28 +- cw_monero/pubspec.lock | 13 +- cw_monero/pubspec.yaml | 4 + pubspec_base.yaml | 5 +- 23 files changed, 573 insertions(+), 839 deletions(-) create mode 100644 Makefile create mode 100644 android/app/src/main/jniLibs/arm64-v8a/.gitkeep create mode 100644 android/app/src/main/jniLibs/armeabi-v7a/.gitkeep create mode 100644 android/app/src/main/jniLibs/x86/.gitkeep create mode 100644 android/app/src/main/jniLibs/x86_64/.gitkeep delete mode 100644 cw_monero/lib/api/convert_utf8_to_string.dart delete mode 100644 cw_monero/lib/api/monero_api.dart delete mode 100644 cw_monero/lib/api/signatures.dart diff --git a/Makefile b/Makefile new file mode 100644 index 000000000..f8205ab9d --- /dev/null +++ b/Makefile @@ -0,0 +1,120 @@ +# TODO(mrcyjanek): Cleanup, this is borrowed from unnamed_monero_wallet repo. + +MONERO_C_TAG=v0.18.3.3-RC21 +LIBCPP_SHARED_SO_TAG=latest-RC1 +LIBCPP_SHARED_SO_NDKVERSION=r17c + +.PHONY: android +android: + ./build_changelog.sh + flutter build apk --split-per-abi --flavor calc --dart-define=libstealth_calculator=true + flutter build apk --split-per-abi --flavor clean --dart-define=libstealth_calculator=false + +.PHONY: linux +linux: + ./build_changelog.sh + flutter build linux + echo https://static.mrcyjanek.net/monero_c/${MONERO_C_TAG}/${TARGET_TRIPLET}_libwallet2_api_c.so.xz + wget https://static.mrcyjanek.net/monero_c/${MONERO_C_TAG}/${TARGET_TRIPLET}_libwallet2_api_c.so.xz \ + -O build/linux/${FLUTTER_ARCH}/release/bundle/lib/libwallet2_api_c.so.xz + -rm build/linux/${FLUTTER_ARCH}/release/bundle/lib/libwallet2_api_c.so + unxz build/linux/${FLUTTER_ARCH}/release/bundle/lib/libwallet2_api_c.so.xz + -rm build/linux/${FLUTTER_ARCH}/release/xmruw-linux-${DEBIAN_ARCH}.tar* + (cd build/linux/${FLUTTER_ARCH}/release && cp -a bundle xmruw && tar -cvf xmruw-linux-${DEBIAN_ARCH}.tar xmruw && xz -e xmruw-linux-${DEBIAN_ARCH}.tar) + + +.PHONY: linux_debug_lib +linux_debug_lib: + wget https://static.mrcyjanek.net/monero_c/${MONERO_C_TAG}/${shell gcc -dumpmachine}_libwallet2_api_c.so.xz \ + -O build/linux/${FLUTTER_ARCH}/debug/bundle/lib/libwallet2_api_c.so.xz + -rm build/linux/${FLUTTER_ARCH}/debug/bundle/lib/libwallet2_api_c.so + unxz build/linux/${FLUTTER_ARCH}/debug/bundle/lib/libwallet2_api_c.so.xz + +deb: + dart pub global activate --source git https://github.com/tomekit/flutter_to_debian.git + cat debian/debian.yaml.txt | sed 's/x64/${FLUTTER_ARCH}/g' | sed 's/amd64/${DEBIAN_ARCH}/g' > debian/debian.yaml + ${HOME}/.pub-cache/bin/flutter_to_debian + +.PHONY: dev +dev: libs + +dev: +lib/const/resource.g.dart: + dart pub global activate flutter_asset_generator + timeout 15 ${HOME}/.pub-cache/bin/fgen || true + mv lib/const/resource.dart lib/const/resource.g.dart +.PHONY: lib/const/resource.g.dart + +.PHONY: sailfishos +sailfishos: + ./build_changelog.sh + bash ./elinux/sailfish_build.sh + +.PHONY: version +version: + sed -i "s/^version: .*/version: 1.0.0+$(shell git rev-list --count HEAD)/" "pubspec.yaml" + sed -i "s/^ Version: .*/ Version: 1.0.0+$(shell git rev-list --count HEAD)/" "debian/debian.yaml.txt" + sed -i "s/^Version=.*/Version=1.0.0+$(shell git rev-list --count HEAD)/" "debian/gui/xmruw.desktop" + sed -i "s/^Version=.*/Version=1.0.0+$(shell git rev-list --count HEAD)/" "elinux/unnamed-monero-wallet.desktop" + sed -i "s/^Version: .*/Version: 1.0.0+$(shell git rev-list --count HEAD)/" "elinux/sailfishos.spec" + sed -i "s/^Release: .*/Release: $(shell git rev-list --count HEAD)/" "elinux/sailfishos.spec" + sed -i "s/^Version: .*/Version: 1.0.0+$(shell git rev-list --count HEAD)/" "elinux/sailfishos.spec" + sed -i "s/^const xmruwVersion = .*/const xmruwVersion = '$(shell git describe --tags)';/" "lib/helpers/licenses_extra.dart" + +.PHONY: lib/helpers/licenses.g.dart +lib/helpers/licenses.g.dart: + dart pub run flutter_oss_licenses:generate.dart -o lib/helpers/licenses.g.dart + +libs: android/app/src/main/jniLibs/arm64-v8a/libmonero_libwallet2_api_c.so +.PHONY: android/app/src/main/jniLibs/arm64-v8a/libmonero_libwallet2_api_c.so +android/app/src/main/jniLibs/arm64-v8a/libmonero_libwallet2_api_c.so: + wget -q https://static.mrcyjanek.net/monero_c/${MONERO_C_TAG}/monero/aarch64-linux-android_libwallet2_api_c.so.xz -O android/app/src/main/jniLibs/arm64-v8a/libmonero_libwallet2_api_c.so.xz + unxz android/app/src/main/jniLibs/arm64-v8a/libmonero_libwallet2_api_c.so.xz + +libs: android/app/src/main/jniLibs/arm64-v8a/libc++_shared.so +.PHONY: android/app/src/main/jniLibs/arm64-v8a/libc++_shared.so +android/app/src/main/jniLibs/arm64-v8a/libc++_shared.so: + wget -q https://git.mrcyjanek.net/mrcyjanek/libcpp_shared.so/releases/download/${LIBCPP_SHARED_SO_TAG}/${LIBCPP_SHARED_SO_NDKVERSION}_arm64-v8a_libc++_shared.so -O android/app/src/main/jniLibs/arm64-v8a/libc++_shared.so + +libs: android/app/src/main/jniLibs/armeabi-v7a/libmonero_libwallet2_api_c.so +.PHONY: android/app/src/main/jniLibs/armeabi-v7a/libmonero_libwallet2_api_c.so +android/app/src/main/jniLibs/armeabi-v7a/libmonero_libwallet2_api_c.so: + wget -q https://static.mrcyjanek.net/monero_c/${MONERO_C_TAG}/monero/arm-linux-androideabi_libwallet2_api_c.so.xz -O android/app/src/main/jniLibs/armeabi-v7a/libmonero_libwallet2_api_c.so.xz + unxz android/app/src/main/jniLibs/armeabi-v7a/libmonero_libwallet2_api_c.so.xz + +libs: android/app/src/main/jniLibs/armeabi-v7a/libc++_shared.so +.PHONY: android/app/src/main/jniLibs/armeabi-v7a/libc++_shared.so +android/app/src/main/jniLibs/armeabi-v7a/libc++_shared.so: + wget -q https://git.mrcyjanek.net/mrcyjanek/libcpp_shared.so/releases/download/${LIBCPP_SHARED_SO_TAG}/${LIBCPP_SHARED_SO_NDKVERSION}_armeabi-v7a_libc++_shared.so -O android/app/src/main/jniLibs/armeabi-v7a/libc++_shared.so + +# libs: android/app/src/main/jniLibs/x86/libmonero_libwallet2_api_c.so +# .PHONY: android/app/src/main/jniLibs/x86/libmonero_libwallet2_api_c.so +# android/app/src/main/jniLibs/x86/libmonero_libwallet2_api_c.so: +# wget -q https://static.mrcyjanek.net/monero_c/${MONERO_C_TAG}/monero/i686-linux-android_libwallet2_api_c.so.xz -O android/app/src/main/jniLibs/x86/libmonero_libwallet2_api_c.so.xz +# unxz android/app/src/main/jniLibs/x86/libmonero_libwallet2_api_c.so.xz + +libs: android/app/src/main/jniLibs/x86/libc++_shared.so +.PHONY: android/app/src/main/jniLibs/x86/libc++_shared.so +android/app/src/main/jniLibs/x86/libc++_shared.so: + wget -q https://git.mrcyjanek.net/mrcyjanek/libcpp_shared.so/releases/download/${LIBCPP_SHARED_SO_TAG}/${LIBCPP_SHARED_SO_NDKVERSION}_x86_libc++_shared.so -O android/app/src/main/jniLibs/x86/libc++_shared.so + +libs: android/app/src/main/jniLibs/x86_64/libmonero_libwallet2_api_c.so +.PHONY: android/app/src/main/jniLibs/x86_64/libmonero_libwallet2_api_c.so +android/app/src/main/jniLibs/x86_64/libmonero_libwallet2_api_c.so: + wget -q https://static.mrcyjanek.net/monero_c/${MONERO_C_TAG}/monero/x86_64-linux-android_libwallet2_api_c.so.xz -O android/app/src/main/jniLibs/x86_64/libmonero_libwallet2_api_c.so.xz + unxz android/app/src/main/jniLibs/x86_64/libmonero_libwallet2_api_c.so.xz + +libs: android/app/src/main/jniLibs/x86_64/libc++_shared.so +.PHONY: android/app/src/main/jniLibs/x86_64/libc++_shared.so +android/app/src/main/jniLibs/x86_64/libc++_shared.so: + wget -q https://git.mrcyjanek.net/mrcyjanek/libcpp_shared.so/releases/download/${LIBCPP_SHARED_SO_TAG}/${LIBCPP_SHARED_SO_NDKVERSION}_x86_64_libc++_shared.so -O android/app/src/main/jniLibs/x86_64/libc++_shared.so + +clean_libs: + -rm android/app/src/main/jniLibs/x86_64/libc++_shared.so* + -rm android/app/src/main/jniLibs/x86_64/*_libwallet2_api_c.so* + -rm android/app/src/main/jniLibs/armeabi-v7a/libc++_shared.so* + -rm android/app/src/main/jniLibs/armeabi-v7a/*_libwallet2_api_c.so* + -rm android/app/src/main/jniLibs/x86/libc++_shared.so* + -rm android/app/src/main/jniLibs/x86/*_libwallet2_api_c.so* + -rm android/app/src/main/jniLibs/arm64-v8a/libc++_shared.so* + -rm android/app/src/main/jniLibs/arm64-v8a/*_libwallet2_api_c.so* \ No newline at end of file diff --git a/android/app/src/main/AndroidManifestBase.xml b/android/app/src/main/AndroidManifestBase.xml index eea9b5521..ba7868f9e 100644 --- a/android/app/src/main/AndroidManifestBase.xml +++ b/android/app/src/main/AndroidManifestBase.xml @@ -18,7 +18,8 @@ android:fullBackupContent="false" android:versionCode="__versionCode__" android:versionName="__versionName__" - android:requestLegacyExternalStorage="true"> + android:requestLegacyExternalStorage="true" + android:extractNativeLibs="true"> args) { } Future addAccount({required String label}) async { - await compute(_addAccount, label); + _addAccount(label); await store(); } Future setLabelForAccount({required int accountIndex, required String label}) async { - await compute( - _setLabelForAccount, {'accountIndex': accountIndex, 'label': label}); + _setLabelForAccount({'accountIndex': accountIndex, 'label': label}); await store(); } \ No newline at end of file diff --git a/cw_monero/example/pubspec.lock b/cw_monero/example/pubspec.lock index c9ca8d92b..e2dd94d7a 100644 --- a/cw_monero/example/pubspec.lock +++ b/cw_monero/example/pubspec.lock @@ -115,10 +115,10 @@ packages: dependency: transitive description: name: ffi - sha256: a38574032c5f1dd06c4aee541789906c12ccaab8ba01446e800d9c5b79c4a978 + sha256: "7bf0adc28a23d395f19f3f1eb21dd7cfd1dd9f8e1c50051c069122e6853bc878" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.1.0" file: dependency: transitive description: @@ -241,6 +241,15 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.3+1" + monero: + dependency: transitive + description: + path: "." + ref: master + resolved-ref: "08c5a32cbcf1f04dbae5826c83abda8fb0dbdcce" + url: "https://git.mrcyjanek.net/mrcyjanek/monero.dart" + source: git + version: "0.0.0" path: dependency: transitive description: diff --git a/cw_monero/lib/api/account_list.dart b/cw_monero/lib/api/account_list.dart index 451ba5033..0a18ca571 100644 --- a/cw_monero/lib/api/account_list.dart +++ b/cw_monero/lib/api/account_list.dart @@ -1,38 +1,16 @@ -import 'dart:ffi'; -import 'package:ffi/ffi.dart'; -import 'package:cw_monero/api/signatures.dart'; -import 'package:cw_monero/api/types.dart'; -import 'package:cw_monero/api/monero_api.dart'; -import 'package:cw_monero/api/structs/account_row.dart'; -import 'package:flutter/foundation.dart'; import 'package:cw_monero/api/wallet.dart'; +import 'package:monero/monero.dart' as monero; -final accountSizeNative = moneroApi - .lookup>('account_size') - .asFunction(); - -final accountRefreshNative = moneroApi - .lookup>('account_refresh') - .asFunction(); - -final accountGetAllNative = moneroApi - .lookup>('account_get_all') - .asFunction(); - -final accountAddNewNative = moneroApi - .lookup>('account_add_row') - .asFunction(); - -final accountSetLabelNative = moneroApi - .lookup>('account_set_label_row') - .asFunction(); +monero.wallet? wptr = null; +monero.SubaddressAccount? account; bool isUpdating = false; void refreshAccounts() { try { isUpdating = true; - accountRefreshNative(); + account = monero.Wallet_subaddressAccount(wptr!); + monero.SubaddressAccount_refresh(account!); isUpdating = false; } catch (e) { isUpdating = false; @@ -40,26 +18,22 @@ void refreshAccounts() { } } -List getAllAccount() { - final size = accountSizeNative(); - final accountAddressesPointer = accountGetAllNative(); - final accountAddresses = accountAddressesPointer.asTypedList(size); +List getAllAccount() { + // final size = monero.Wallet_numSubaddressAccounts(wptr!); + final size = monero.SubaddressAccount_getAll_size(wptr!); - return accountAddresses - .map((addr) => Pointer.fromAddress(addr).ref) - .toList(); + return List.generate(size, (index) { + return monero.SubaddressAccount_getAll_byIndex(wptr!, index: index); + }); } void addAccountSync({required String label}) { - final labelPointer = label.toNativeUtf8(); - accountAddNewNative(labelPointer); - calloc.free(labelPointer); + monero.Wallet_addSubaddressAccount(wptr!, label: label); } void setLabelForAccountSync({required int accountIndex, required String label}) { - final labelPointer = label.toNativeUtf8(); - accountSetLabelNative(accountIndex, labelPointer); - calloc.free(labelPointer); + // TODO(mrcyjanek): this may be wrong function? + monero.Wallet_setSubaddressLabel(wptr!, accountIndex: accountIndex, addressIndex: 0, label: label); } void _addAccount(String label) => addAccountSync(label: label); @@ -72,12 +46,11 @@ void _setLabelForAccount(Map args) { } Future addAccount({required String label}) async { - await compute(_addAccount, label); + _addAccount(label); await store(); } Future setLabelForAccount({required int accountIndex, required String label}) async { - await compute( - _setLabelForAccount, {'accountIndex': accountIndex, 'label': label}); + _setLabelForAccount({'accountIndex': accountIndex, 'label': label}); await store(); } \ No newline at end of file diff --git a/cw_monero/lib/api/coins_info.dart b/cw_monero/lib/api/coins_info.dart index d7350a6e2..c1b634cc6 100644 --- a/cw_monero/lib/api/coins_info.dart +++ b/cw_monero/lib/api/coins_info.dart @@ -1,35 +1,17 @@ -import 'dart:ffi'; -import 'package:cw_monero/api/signatures.dart'; -import 'package:cw_monero/api/structs/coins_info_row.dart'; -import 'package:cw_monero/api/types.dart'; -import 'package:cw_monero/api/monero_api.dart'; +import 'package:cw_monero/api/account_list.dart'; +import 'package:monero/monero.dart' as monero; -final refreshCoinsNative = moneroApi - .lookup>('refresh_coins') - .asFunction(); +monero.Coins? coins = null; -final coinsCountNative = moneroApi - .lookup>('coins_count') - .asFunction(); +void refreshCoins(int accountIndex) { + coins = monero.Wallet_coins(wptr!); + monero.Coins_refresh(coins!); +} -final coinNative = moneroApi - .lookup>('coin') - .asFunction(); +int countOfCoins() => monero.Coins_count(coins!); -final freezeCoinNative = moneroApi - .lookup>('freeze_coin') - .asFunction(); +monero.CoinsInfo getCoin(int index) => monero.Coins_coin(coins!, index); -final thawCoinNative = moneroApi - .lookup>('thaw_coin') - .asFunction(); +void freezeCoin(int index) => monero.Coins_setFrozen(coins!, index: index); -void refreshCoins(int accountIndex) => refreshCoinsNative(accountIndex); - -int countOfCoins() => coinsCountNative(); - -CoinsInfoRow getCoin(int index) => coinNative(index).ref; - -void freezeCoin(int index) => freezeCoinNative(index); - -void thawCoin(int index) => thawCoinNative(index); +void thawCoin(int index) => monero.Coins_thaw(coins!, index: index); diff --git a/cw_monero/lib/api/convert_utf8_to_string.dart b/cw_monero/lib/api/convert_utf8_to_string.dart deleted file mode 100644 index 41a6b648a..000000000 --- a/cw_monero/lib/api/convert_utf8_to_string.dart +++ /dev/null @@ -1,8 +0,0 @@ -import 'dart:ffi'; -import 'package:ffi/ffi.dart'; - -String convertUTF8ToString({required Pointer pointer}) { - final str = pointer.toDartString(); - calloc.free(pointer); - return str; -} \ No newline at end of file diff --git a/cw_monero/lib/api/monero_api.dart b/cw_monero/lib/api/monero_api.dart deleted file mode 100644 index 398d737d1..000000000 --- a/cw_monero/lib/api/monero_api.dart +++ /dev/null @@ -1,6 +0,0 @@ -import 'dart:ffi'; -import 'dart:io'; - -final DynamicLibrary moneroApi = Platform.isAndroid - ? DynamicLibrary.open("libcw_monero.so") - : DynamicLibrary.open("cw_monero.framework/cw_monero"); \ No newline at end of file diff --git a/cw_monero/lib/api/signatures.dart b/cw_monero/lib/api/signatures.dart deleted file mode 100644 index bc4fc9d38..000000000 --- a/cw_monero/lib/api/signatures.dart +++ /dev/null @@ -1,153 +0,0 @@ -import 'dart:ffi'; -import 'package:cw_monero/api/structs/coins_info_row.dart'; -import 'package:cw_monero/api/structs/pending_transaction.dart'; -import 'package:cw_monero/api/structs/transaction_info_row.dart'; -import 'package:cw_monero/api/structs/ut8_box.dart'; -import 'package:ffi/ffi.dart'; - -typedef create_wallet = Int8 Function( - Pointer, Pointer, Pointer, Int32, Pointer); - -typedef restore_wallet_from_seed = Int8 Function( - Pointer, Pointer, Pointer, Int32, Int64, Pointer); - -typedef restore_wallet_from_keys = Int8 Function(Pointer, Pointer, Pointer, - Pointer, Pointer, Pointer, Int32, Int64, Pointer); - -typedef restore_wallet_from_spend_key = Int8 Function(Pointer, Pointer, Pointer, - Pointer, Pointer, Int32, Int64, Pointer); - -typedef is_wallet_exist = Int8 Function(Pointer); - -typedef load_wallet = Int8 Function(Pointer, Pointer, Int8); - -typedef error_string = Pointer Function(); - -typedef get_filename = Pointer Function(); - -typedef get_seed = Pointer Function(); - -typedef get_address = Pointer Function(Int32, Int32); - -typedef get_full_balanace = Int64 Function(Int32); - -typedef get_unlocked_balanace = Int64 Function(Int32); - -typedef get_current_height = Int64 Function(); - -typedef get_node_height = Int64 Function(); - -typedef is_connected = Int8 Function(); - -typedef setup_node = Int8 Function( - Pointer, Pointer?, Pointer?, Int8, Int8, Pointer?, Pointer); - -typedef start_refresh = Void Function(); - -typedef connect_to_node = Int8 Function(); - -typedef set_refresh_from_block_height = Void Function(Int64); - -typedef set_recovering_from_seed = Void Function(Int8); - -typedef store_c = Void Function(Pointer); - -typedef set_password = Int8 Function(Pointer password, Pointer error); - -typedef set_listener = Void Function(); - -typedef get_syncing_height = Int64 Function(); - -typedef is_needed_to_refresh = Int8 Function(); - -typedef is_new_transaction_exist = Int8 Function(); - -typedef subaddrress_size = Int32 Function(); - -typedef subaddrress_refresh = Void Function(Int32); - -typedef subaddress_get_all = Pointer Function(); - -typedef subaddress_add_new = Void Function(Int32 accountIndex, Pointer label); - -typedef subaddress_set_label = Void Function( - Int32 accountIndex, Int32 addressIndex, Pointer label); - -typedef account_size = Int32 Function(); - -typedef account_refresh = Void Function(); - -typedef account_get_all = Pointer Function(); - -typedef account_add_new = Void Function(Pointer label); - -typedef account_set_label = Void Function(Int32 accountIndex, Pointer label); - -typedef transactions_refresh = Void Function(); - -typedef get_transaction = Pointer Function(Pointer txId); - -typedef get_tx_key = Pointer? Function(Pointer txId); - -typedef transactions_count = Int64 Function(); - -typedef transactions_get_all = Pointer Function(); - -typedef transaction_create = Int8 Function( - Pointer address, - Pointer paymentId, - Pointer amount, - Int8 priorityRaw, - Int32 subaddrAccount, - Pointer> preferredInputs, - Int32 preferredInputsSize, - Pointer error, - Pointer pendingTransaction); - -typedef transaction_create_mult_dest = Int8 Function( - Pointer> addresses, - Pointer paymentId, - Pointer> amounts, - Int32 size, - Int8 priorityRaw, - Int32 subaddrAccount, - Pointer> preferredInputs, - Int32 preferredInputsSize, - Pointer error, - Pointer pendingTransaction); - -typedef transaction_commit = Int8 Function(Pointer, Pointer); - -typedef secret_view_key = Pointer Function(); - -typedef public_view_key = Pointer Function(); - -typedef secret_spend_key = Pointer Function(); - -typedef public_spend_key = Pointer Function(); - -typedef close_current_wallet = Void Function(); - -typedef on_startup = Void Function(); - -typedef rescan_blockchain = Void Function(); - -typedef get_subaddress_label = Pointer Function(Int32 accountIndex, Int32 addressIndex); - -typedef set_trusted_daemon = Void Function(Int8 trusted); - -typedef trusted_daemon = Int8 Function(); - -typedef refresh_coins = Void Function(Int32 accountIndex); - -typedef coins_count = Int64 Function(); - -// typedef coins_from_txid = Pointer Function(Pointer txid); - -typedef coin = Pointer Function(Int32 index); - -typedef freeze_coin = Void Function(Int32 index); - -typedef thaw_coin = Void Function(Int32 index); - -typedef sign_message = Pointer Function(Pointer message, Pointer address); diff --git a/cw_monero/lib/api/subaddress_list.dart b/cw_monero/lib/api/subaddress_list.dart index 1c1f1253f..7ac44b1b5 100644 --- a/cw_monero/lib/api/subaddress_list.dart +++ b/cw_monero/lib/api/subaddress_list.dart @@ -1,38 +1,14 @@ -import 'dart:ffi'; -import 'package:ffi/ffi.dart'; -import 'package:flutter/foundation.dart'; -import 'package:cw_monero/api/signatures.dart'; -import 'package:cw_monero/api/types.dart'; -import 'package:cw_monero/api/monero_api.dart'; -import 'package:cw_monero/api/structs/subaddress_row.dart'; +import 'package:cw_monero/api/account_list.dart'; import 'package:cw_monero/api/wallet.dart'; - -final subaddressSizeNative = moneroApi - .lookup>('subaddrress_size') - .asFunction(); - -final subaddressRefreshNative = moneroApi - .lookup>('subaddress_refresh') - .asFunction(); - -final subaddrressGetAllNative = moneroApi - .lookup>('subaddrress_get_all') - .asFunction(); - -final subaddrressAddNewNative = moneroApi - .lookup>('subaddress_add_row') - .asFunction(); - -final subaddrressSetLabelNative = moneroApi - .lookup>('subaddress_set_label') - .asFunction(); +import 'package:monero/monero.dart' as monero; bool isUpdating = false; - +monero.AddressBook? addressbook = null; void refreshSubaddresses({required int accountIndex}) { try { isUpdating = true; - subaddressRefreshNative(accountIndex); + addressbook = monero.Wallet_subaddressAccount(wptr!); + monero.AddressBook_refresh(addressbook!); isUpdating = false; } catch (e) { isUpdating = false; @@ -40,28 +16,21 @@ void refreshSubaddresses({required int accountIndex}) { } } -List getAllSubaddresses() { - final size = subaddressSizeNative(); - final subaddressAddressesPointer = subaddrressGetAllNative(); - final subaddressAddresses = subaddressAddressesPointer.asTypedList(size); +List getAllSubaddresses() { + final size = monero.AddressBook_getAll_size(addressbook!); - return subaddressAddresses - .map((addr) => Pointer.fromAddress(addr).ref) - .toList(); + return List.generate(size, (index) { + return monero.Subaddress_getAll_byIndex(wptr!, index: index); + }); } void addSubaddressSync({required int accountIndex, required String label}) { - final labelPointer = label.toNativeUtf8(); - subaddrressAddNewNative(accountIndex, labelPointer); - calloc.free(labelPointer); + monero.Wallet_addSubaddress(wptr!, accountIndex: accountIndex, label: label); } void setLabelForSubaddressSync( {required int accountIndex, required int addressIndex, required String label}) { - final labelPointer = label.toNativeUtf8(); - - subaddrressSetLabelNative(accountIndex, addressIndex, labelPointer); - calloc.free(labelPointer); + monero.Wallet_setSubaddressLabel(wptr!, accountIndex: accountIndex, addressIndex: addressIndex, label: label); } void _addSubaddress(Map args) { @@ -81,14 +50,13 @@ void _setLabelForSubaddress(Map args) { } Future addSubaddress({required int accountIndex, required String label}) async { - await compute, void>( - _addSubaddress, {'accountIndex': accountIndex, 'label': label}); - await store(); + _addSubaddress({'accountIndex': accountIndex, 'label': label}); + await store(); } Future setLabelForSubaddress( {required int accountIndex, required int addressIndex, required String label}) async { - await compute, void>(_setLabelForSubaddress, { + _setLabelForSubaddress({ 'accountIndex': accountIndex, 'addressIndex': addressIndex, 'label': label diff --git a/cw_monero/lib/api/transaction_history.dart b/cw_monero/lib/api/transaction_history.dart index 73c8de801..4dff7dfe3 100644 --- a/cw_monero/lib/api/transaction_history.dart +++ b/cw_monero/lib/api/transaction_history.dart @@ -1,78 +1,35 @@ import 'dart:ffi'; -import 'package:cw_monero/api/convert_utf8_to_string.dart'; +import 'package:cw_monero/api/account_list.dart'; import 'package:cw_monero/api/exceptions/creation_transaction_exception.dart'; -import 'package:cw_monero/api/monero_api.dart'; import 'package:cw_monero/api/monero_output.dart'; -import 'package:cw_monero/api/signatures.dart'; import 'package:cw_monero/api/structs/pending_transaction.dart'; -import 'package:cw_monero/api/structs/transaction_info_row.dart'; -import 'package:cw_monero/api/structs/ut8_box.dart'; -import 'package:cw_monero/api/types.dart'; import 'package:ffi/ffi.dart'; -import 'package:flutter/foundation.dart'; +import 'package:monero/monero.dart' as monero; -final transactionsRefreshNative = moneroApi - .lookup>('transactions_refresh') - .asFunction(); - -final transactionsCountNative = moneroApi - .lookup>('transactions_count') - .asFunction(); - -final transactionsGetAllNative = moneroApi - .lookup>('transactions_get_all') - .asFunction(); - -final transactionCreateNative = moneroApi - .lookup>('transaction_create') - .asFunction(); - -final transactionCreateMultDestNative = moneroApi - .lookup>('transaction_create_mult_dest') - .asFunction(); - -final transactionCommitNative = moneroApi - .lookup>('transaction_commit') - .asFunction(); - -final getTxKeyNative = - moneroApi.lookup>('get_tx_key').asFunction(); - -final getTransactionNative = moneroApi - .lookup>('get_transaction') - .asFunction(); String getTxKey(String txId) { - final txIdPointer = txId.toNativeUtf8(); - final keyPointer = getTxKeyNative(txIdPointer); - - calloc.free(txIdPointer); - - if (keyPointer != null) { - return convertUTF8ToString(pointer: keyPointer); - } - - return ''; + return monero.Wallet_getTxKey(wptr!, txid: txId); } -void refreshTransactions() => transactionsRefreshNative(); +monero.TransactionHistory? txhistory; -int countOfTransactions() => transactionsCountNative(); - -List getAllTransactions() { - final size = transactionsCountNative(); - final transactionsPointer = transactionsGetAllNative(); - final transactionsAddresses = transactionsPointer.asTypedList(size); - - return transactionsAddresses - .map((addr) => Pointer.fromAddress(addr).ref) - .toList(); +void refreshTransactions() { + txhistory = monero.Wallet_history(wptr!); + monero.TransactionHistory_refresh(txhistory!); } -TransactionInfoRow getTransaction(String txId) { - final txIdPointer = txId.toNativeUtf8(); - return getTransactionNative(txIdPointer).ref; +int countOfTransactions() => monero.TransactionHistory_count(txhistory!); + +List getAllTransactions() { + final size = countOfTransactions(); + + return List.generate(size, (index) => Transaction(txInfo: monero.TransactionHistory_transaction(txhistory!, index: index))); +} + +// TODO(mrcyjanek): ... +Transaction getTransaction(String txId) { + return Transaction(txInfo: monero.TransactionHistory_transactionById(txhistory!, txid: txId)); } PendingTransactionDescription createTransactionSync( @@ -82,8 +39,6 @@ PendingTransactionDescription createTransactionSync( String? amount, int accountIndex = 0, List preferredInputs = const []}) { - final addressPointer = address.toNativeUtf8(); - final paymentIdPointer = paymentId.toNativeUtf8(); final amountPointer = amount != null ? amount.toNativeUtf8() : nullptr; final int preferredInputsSize = preferredInputs.length; @@ -95,44 +50,38 @@ PendingTransactionDescription createTransactionSync( preferredInputsPointerPointer[i] = preferredInputsPointers[i]; } - final errorMessagePointer = calloc(); - final pendingTransactionRawPointer = calloc(); - final created = transactionCreateNative( - addressPointer, - paymentIdPointer, - amountPointer, - priorityRaw, - accountIndex, - preferredInputsPointerPointer, - preferredInputsSize, - errorMessagePointer, - pendingTransactionRawPointer) != - 0; + final amt = amount == null ? 0 : monero.Wallet_amountFromString(amount); + final pendingTx = monero.Wallet_createTransaction( + wptr!, + dst_addr: address, + payment_id: paymentId, + amount: amt, + mixin_count: 1, + pendingTransactionPriority: priorityRaw, + subaddr_account: accountIndex, + preferredInputs: preferredInputs, + ); + final String? error = (() { + final status = monero.Wallet_status(wptr!); + if (status == 0) { + return null; + } + return monero.Wallet_errorString(wptr!); + })(); - calloc.free(preferredInputsPointerPointer); - - preferredInputsPointers.forEach((element) => calloc.free(element)); - - calloc.free(addressPointer); - calloc.free(paymentIdPointer); - - if (amountPointer != nullptr) { - calloc.free(amountPointer); - } - - if (!created) { - final message = errorMessagePointer.ref.getValue(); - calloc.free(errorMessagePointer); + if (error != null) { + final message = error; throw CreationTransactionException(message: message); } return PendingTransactionDescription( - amount: pendingTransactionRawPointer.ref.amount, - fee: pendingTransactionRawPointer.ref.fee, - hash: pendingTransactionRawPointer.ref.getHash(), - hex: pendingTransactionRawPointer.ref.getHex(), - txKey: pendingTransactionRawPointer.ref.getKey(), - pointerAddress: pendingTransactionRawPointer.address); + amount: monero.PendingTransaction_amount(wptr!), + fee: monero.PendingTransaction_fee(wptr!), + hash: monero.PendingTransaction_txid(wptr!, ''), + hex: '', + txKey: monero.PendingTransaction_txid(wptr!, ''), + pointerAddress: pendingTx.address, + ); } PendingTransactionDescription createTransactionMultDestSync( @@ -141,80 +90,88 @@ PendingTransactionDescription createTransactionMultDestSync( required int priorityRaw, int accountIndex = 0, List preferredInputs = const []}) { - final int size = outputs.length; - final List> addressesPointers = - outputs.map((output) => output.address.toNativeUtf8()).toList(); - final Pointer> addressesPointerPointer = calloc(size); - final List> amountsPointers = - outputs.map((output) => output.amount.toNativeUtf8()).toList(); - final Pointer> amountsPointerPointer = calloc(size); + // final int size = outputs.length; + // final List> addressesPointers = + // outputs.map((output) => output.address.toNativeUtf8()).toList(); + // final Pointer> addressesPointerPointer = calloc(size); + // final List> amountsPointers = + // outputs.map((output) => output.amount.toNativeUtf8()).toList(); + // final Pointer> amountsPointerPointer = calloc(size); - for (int i = 0; i < size; i++) { - addressesPointerPointer[i] = addressesPointers[i]; - amountsPointerPointer[i] = amountsPointers[i]; - } + // for (int i = 0; i < size; i++) { + // addressesPointerPointer[i] = addressesPointers[i]; + // amountsPointerPointer[i] = amountsPointers[i]; + // } - final int preferredInputsSize = preferredInputs.length; - final List> preferredInputsPointers = - preferredInputs.map((output) => output.toNativeUtf8()).toList(); - final Pointer> preferredInputsPointerPointer = calloc(preferredInputsSize); + // final int preferredInputsSize = preferredInputs.length; + // final List> preferredInputsPointers = + // preferredInputs.map((output) => output.toNativeUtf8()).toList(); + // final Pointer> preferredInputsPointerPointer = calloc(preferredInputsSize); - for (int i = 0; i < preferredInputsSize; i++) { - preferredInputsPointerPointer[i] = preferredInputsPointers[i]; - } + // for (int i = 0; i < preferredInputsSize; i++) { + // preferredInputsPointerPointer[i] = preferredInputsPointers[i]; + // } - final paymentIdPointer = paymentId.toNativeUtf8(); - final errorMessagePointer = calloc(); - final pendingTransactionRawPointer = calloc(); - final created = transactionCreateMultDestNative( - addressesPointerPointer, - paymentIdPointer, - amountsPointerPointer, - size, - priorityRaw, - accountIndex, - preferredInputsPointerPointer, - preferredInputsSize, - errorMessagePointer, - pendingTransactionRawPointer) != - 0; + // final paymentIdPointer = paymentId.toNativeUtf8(); + // final errorMessagePointer = calloc(); + // final pendingTransactionRawPointer = calloc(); + // final created = transactionCreateMultDestNative( + // addressesPointerPointer, + // paymentIdPointer, + // amountsPointerPointer, + // size, + // priorityRaw, + // accountIndex, + // preferredInputsPointerPointer, + // preferredInputsSize, + // errorMessagePointer, + // pendingTransactionRawPointer) != + // 0; - calloc.free(addressesPointerPointer); - calloc.free(amountsPointerPointer); - calloc.free(preferredInputsPointerPointer); + // calloc.free(addressesPointerPointer); + // calloc.free(amountsPointerPointer); + // calloc.free(preferredInputsPointerPointer); - addressesPointers.forEach((element) => calloc.free(element)); - amountsPointers.forEach((element) => calloc.free(element)); - preferredInputsPointers.forEach((element) => calloc.free(element)); + // addressesPointers.forEach((element) => calloc.free(element)); + // amountsPointers.forEach((element) => calloc.free(element)); + // preferredInputsPointers.forEach((element) => calloc.free(element)); - calloc.free(paymentIdPointer); + // calloc.free(paymentIdPointer); - if (!created) { - final message = errorMessagePointer.ref.getValue(); - calloc.free(errorMessagePointer); - throw CreationTransactionException(message: message); - } + // if (!created) { + // final message = errorMessagePointer.ref.getValue(); + // calloc.free(errorMessagePointer); + // throw CreationTransactionException(message: message); + // } - return PendingTransactionDescription( - amount: pendingTransactionRawPointer.ref.amount, - fee: pendingTransactionRawPointer.ref.fee, - hash: pendingTransactionRawPointer.ref.getHash(), - hex: pendingTransactionRawPointer.ref.getHex(), - txKey: pendingTransactionRawPointer.ref.getKey(), - pointerAddress: pendingTransactionRawPointer.address); + // return PendingTransactionDescription( + // amount: pendingTransactionRawPointer.ref.amount, + // fee: pendingTransactionRawPointer.ref.fee, + // hash: pendingTransactionRawPointer.ref.getHash(), + // hex: pendingTransactionRawPointer.ref.getHex(), + // txKey: pendingTransactionRawPointer.ref.getKey(), + // pointerAddress: pendingTransactionRawPointer.address); + throw CreationTransactionException(message: "Unimplemented in monero_c"); } void commitTransactionFromPointerAddress({required int address}) => - commitTransaction(transactionPointer: Pointer.fromAddress(address)); + commitTransaction(transactionPointer: monero.PendingTransaction.fromAddress(address)); -void commitTransaction({required Pointer transactionPointer}) { - final errorMessagePointer = calloc(); - final isCommited = transactionCommitNative(transactionPointer, errorMessagePointer) != 0; +void commitTransaction({required monero.PendingTransaction transactionPointer}) { + + final txCommit = monero.PendingTransaction_commit(transactionPointer, filename: '', overwrite: false); + final status = monero.PendingTransaction_status(transactionPointer.cast()); - if (!isCommited) { - final message = errorMessagePointer.ref.getValue(); - calloc.free(errorMessagePointer); - throw CreationTransactionException(message: message); + final String? error = (() { + final status = monero.Wallet_status(wptr!); + if (status == 0) { + return null; + } + return monero.Wallet_errorString(wptr!); + })(); + + if (error != null) { + throw CreationTransactionException(message: error); } } @@ -256,8 +213,8 @@ Future createTransaction( String? amount, String paymentId = '', int accountIndex = 0, - List preferredInputs = const []}) => - compute(_createTransactionSync, { + List preferredInputs = const []}) async => + _createTransactionSync({ 'address': address, 'paymentId': paymentId, 'amount': amount, @@ -271,11 +228,75 @@ Future createTransactionMultDest( required int priorityRaw, String paymentId = '', int accountIndex = 0, - List preferredInputs = const []}) => - compute(_createTransactionMultDestSync, { + List preferredInputs = const []}) async => + _createTransactionMultDestSync({ 'outputs': outputs, 'paymentId': paymentId, 'priorityRaw': priorityRaw, 'accountIndex': accountIndex, 'preferredInputs': preferredInputs }); + + +class Transaction { + final String displayLabel; + String subaddressLabel = monero.Wallet_getSubaddressLabel(wptr!, accountIndex: 0, addressIndex: 0); + late final String address = monero.Wallet_address( + wptr!, + accountIndex: 0, + addressIndex: 0, + ); + final String description; + final int fee; + final int confirmations; + late final bool isPending = confirmations < 10; + final int blockheight; + final int accountIndex; + final String paymentId; + final int amount; + final bool isSpend; + late DateTime timeStamp; + late final bool isConfirmed = !isPending; + final String hash; + + Map toJson() { + return { + "displayLabel": displayLabel, + "subaddressLabel": subaddressLabel, + "address": address, + "description": description, + "fee": fee, + "confirmations": confirmations, + "isPending": isPending, + "blockheight": blockheight, + "accountIndex": accountIndex, + "paymentId": paymentId, + "amount": amount, + "isSpend": isSpend, + "timeStamp": timeStamp.toIso8601String(), + "isConfirmed": isConfirmed, + "hash": hash, + }; + } + + // S finalubAddress? subAddress; + // List transfers = []; + // final int txIndex; + final monero.TransactionInfo txInfo; + Transaction({ + required this.txInfo, + }) : displayLabel = monero.TransactionInfo_label(txInfo), + hash = monero.TransactionInfo_hash(txInfo), + timeStamp = DateTime.fromMillisecondsSinceEpoch( + monero.TransactionInfo_timestamp(txInfo) * 1000, + ), + isSpend = monero.TransactionInfo_direction(txInfo) == + monero.TransactionInfo_Direction.Out, + amount = monero.TransactionInfo_amount(txInfo), + paymentId = monero.TransactionInfo_paymentId(txInfo), + accountIndex = monero.TransactionInfo_subaddrAccount(txInfo), + blockheight = monero.TransactionInfo_blockHeight(txInfo), + confirmations = monero.TransactionInfo_confirmations(txInfo), + fee = monero.TransactionInfo_fee(txInfo), + description = monero.TransactionInfo_description(txInfo); +} \ No newline at end of file diff --git a/cw_monero/lib/api/wallet.dart b/cw_monero/lib/api/wallet.dart index ffa5fe13b..e543a131a 100644 --- a/cw_monero/lib/api/wallet.dart +++ b/cw_monero/lib/api/wallet.dart @@ -1,160 +1,31 @@ import 'dart:async'; -import 'dart:ffi'; -import 'package:ffi/ffi.dart'; -import 'package:cw_monero/api/structs/ut8_box.dart'; -import 'package:cw_monero/api/convert_utf8_to_string.dart'; -import 'package:cw_monero/api/signatures.dart'; -import 'package:cw_monero/api/types.dart'; -import 'package:cw_monero/api/monero_api.dart'; +import 'package:cw_monero/api/account_list.dart'; import 'package:cw_monero/api/exceptions/setup_wallet_exception.dart'; -import 'package:flutter/foundation.dart'; +import 'package:monero/monero.dart' as monero; int _boolToInt(bool value) => value ? 1 : 0; -final getFileNameNative = moneroApi - .lookup>('get_filename') - .asFunction(); +int getSyncingHeight() => monero.Wallet_blockChainHeight(wptr!); -final getSeedNative = - moneroApi.lookup>('seed').asFunction(); +bool isNeededToRefresh() => false; // TODO(mrcyjanek): ? -final getAddressNative = moneroApi - .lookup>('get_address') - .asFunction(); +bool isNewTransactionExist() => false; -final getFullBalanceNative = moneroApi - .lookup>('get_full_balance') - .asFunction(); +String getFilename() => monero.Wallet_filename(wptr!); -final getUnlockedBalanceNative = moneroApi - .lookup>('get_unlocked_balance') - .asFunction(); +String getSeed() => monero.Wallet_seed(wptr!, seedOffset: ''); -final getCurrentHeightNative = moneroApi - .lookup>('get_current_height') - .asFunction(); +String getAddress({int accountIndex = 0, int addressIndex = 0}) => monero.Wallet_address(wptr!, accountIndex: accountIndex, addressIndex: addressIndex); -final getNodeHeightNative = moneroApi - .lookup>('get_node_height') - .asFunction(); +int getFullBalance({int accountIndex = 0}) => monero.Wallet_balance(wptr!, accountIndex: accountIndex); -final isConnectedNative = moneroApi - .lookup>('is_connected') - .asFunction(); +int getUnlockedBalance({int accountIndex = 0}) => monero.Wallet_unlockedBalance(wptr!, accountIndex: accountIndex); -final setupNodeNative = moneroApi - .lookup>('setup_node') - .asFunction(); +int getCurrentHeight() => monero.Wallet_blockChainHeight(wptr!); -final startRefreshNative = moneroApi - .lookup>('start_refresh') - .asFunction(); +int getNodeHeightSync() => monero.Wallet_daemonBlockChainHeight(wptr!); -final connecToNodeNative = moneroApi - .lookup>('connect_to_node') - .asFunction(); - -final setRefreshFromBlockHeightNative = moneroApi - .lookup>( - 'set_refresh_from_block_height') - .asFunction(); - -final setRecoveringFromSeedNative = moneroApi - .lookup>( - 'set_recovering_from_seed') - .asFunction(); - -final storeNative = - moneroApi.lookup>('store').asFunction(); - -final setPasswordNative = - moneroApi.lookup>('set_password').asFunction(); - -final setListenerNative = moneroApi - .lookup>('set_listener') - .asFunction(); - -final getSyncingHeightNative = moneroApi - .lookup>('get_syncing_height') - .asFunction(); - -final isNeededToRefreshNative = moneroApi - .lookup>('is_needed_to_refresh') - .asFunction(); - -final isNewTransactionExistNative = moneroApi - .lookup>( - 'is_new_transaction_exist') - .asFunction(); - -final getSecretViewKeyNative = moneroApi - .lookup>('secret_view_key') - .asFunction(); - -final getPublicViewKeyNative = moneroApi - .lookup>('public_view_key') - .asFunction(); - -final getSecretSpendKeyNative = moneroApi - .lookup>('secret_spend_key') - .asFunction(); - -final getPublicSpendKeyNative = moneroApi - .lookup>('public_spend_key') - .asFunction(); - -final closeCurrentWalletNative = moneroApi - .lookup>('close_current_wallet') - .asFunction(); - -final onStartupNative = moneroApi - .lookup>('on_startup') - .asFunction(); - -final rescanBlockchainAsyncNative = moneroApi - .lookup>('rescan_blockchain') - .asFunction(); - -final getSubaddressLabelNative = moneroApi - .lookup>('get_subaddress_label') - .asFunction(); - -final setTrustedDaemonNative = moneroApi - .lookup>('set_trusted_daemon') - .asFunction(); - -final trustedDaemonNative = moneroApi - .lookup>('trusted_daemon') - .asFunction(); - -final signMessageNative = moneroApi - .lookup>('sign_message') - .asFunction(); - -int getSyncingHeight() => getSyncingHeightNative(); - -bool isNeededToRefresh() => isNeededToRefreshNative() != 0; - -bool isNewTransactionExist() => isNewTransactionExistNative() != 0; - -String getFilename() => convertUTF8ToString(pointer: getFileNameNative()); - -String getSeed() => convertUTF8ToString(pointer: getSeedNative()); - -String getAddress({int accountIndex = 0, int addressIndex = 0}) => - convertUTF8ToString(pointer: getAddressNative(accountIndex, addressIndex)); - -int getFullBalance({int accountIndex = 0}) => - getFullBalanceNative(accountIndex); - -int getUnlockedBalance({int accountIndex = 0}) => - getUnlockedBalanceNative(accountIndex); - -int getCurrentHeight() => getCurrentHeightNative(); - -int getNodeHeightSync() => getNodeHeightNative(); - -bool isConnectedSync() => isConnectedNative() != 0; +bool isConnectedSync() => monero.Wallet_connected(wptr!) != 0; bool setupNodeSync( {required String address, @@ -163,96 +34,64 @@ bool setupNodeSync( bool useSSL = false, bool isLightWallet = false, String? socksProxyAddress}) { - final addressPointer = address.toNativeUtf8(); - Pointer? loginPointer; - Pointer? socksProxyAddressPointer; - Pointer? passwordPointer; - if (login != null) { - loginPointer = login.toNativeUtf8(); + monero.Wallet_init( + wptr!, + daemonAddress: address, + useSsl: useSSL, + proxyAddress: socksProxyAddress ?? '', + daemonUsername: login ?? '', + daemonPassword: password ?? '' + ); + // monero.Wallet_init3(wptr!, argv0: '', defaultLogBaseName: 'moneroc', console: true); + monero.Wallet_startRefresh(wptr!); + monero.Wallet_refreshAsync(wptr!); + + final status = monero.Wallet_status(wptr!); + + if (status == 0) { + throw SetupWalletException(message: monero.Wallet_errorString(wptr!)); } - if (password != null) { - passwordPointer = password.toNativeUtf8(); - } - - if (socksProxyAddress != null) { - socksProxyAddressPointer = socksProxyAddress.toNativeUtf8(); - } - - final errorMessagePointer = ''.toNativeUtf8(); - final isSetupNode = setupNodeNative( - addressPointer, - loginPointer, - passwordPointer, - _boolToInt(useSSL), - _boolToInt(isLightWallet), - socksProxyAddressPointer, - errorMessagePointer) != - 0; - - calloc.free(addressPointer); - - if (loginPointer != null) { - calloc.free(loginPointer); - } - - if (passwordPointer != null) { - calloc.free(passwordPointer); - } - - if (!isSetupNode) { - throw SetupWalletException( - message: convertUTF8ToString(pointer: errorMessagePointer)); - } - - return isSetupNode; + return status == 0; } -void startRefreshSync() => startRefreshNative(); +void startRefreshSync() {} -Future connectToNode() async => connecToNodeNative() != 0; +Future connectToNode() async { + return true; +} -void setRefreshFromBlockHeight({required int height}) => - setRefreshFromBlockHeightNative(height); +void setRefreshFromBlockHeight({required int height}) => monero.Wallet_setRefreshFromBlockHeight(wptr!, refresh_from_block_height: height); -void setRecoveringFromSeed({required bool isRecovery}) => - setRecoveringFromSeedNative(_boolToInt(isRecovery)); +void setRecoveringFromSeed({required bool isRecovery}) => monero.Wallet_setRecoveringFromSeed(wptr!, recoveringFromSeed: isRecovery); void storeSync() { - final pathPointer = ''.toNativeUtf8(); - storeNative(pathPointer); - calloc.free(pathPointer); + monero.Wallet_store(wptr!); } void setPasswordSync(String password) { - final passwordPointer = password.toNativeUtf8(); - final errorMessagePointer = calloc(); - final changed = setPasswordNative(passwordPointer, errorMessagePointer) != 0; - calloc.free(passwordPointer); - - if (!changed) { - final message = errorMessagePointer.ref.getValue(); - calloc.free(errorMessagePointer); - throw Exception(message); - } + monero.Wallet_setPassword(wptr!, password: password); - calloc.free(errorMessagePointer); + + final status = monero.Wallet_status(wptr!); + if (status == 0) { + throw Exception(monero.Wallet_errorString(wptr!)); + } } -void closeCurrentWallet() => closeCurrentWalletNative(); +void closeCurrentWallet() { + monero.Wallet_store(wptr!); + monero.Wallet_stop(wptr!); +} -String getSecretViewKey() => - convertUTF8ToString(pointer: getSecretViewKeyNative()); +String getSecretViewKey() => monero.Wallet_secretViewKey(wptr!); -String getPublicViewKey() => - convertUTF8ToString(pointer: getPublicViewKeyNative()); +String getPublicViewKey() => monero.Wallet_publicViewKey(wptr!); -String getSecretSpendKey() => - convertUTF8ToString(pointer: getSecretSpendKeyNative()); +String getSecretSpendKey() => monero.Wallet_secretSpendKey(wptr!); -String getPublicSpendKey() => - convertUTF8ToString(pointer: getPublicSpendKeyNative()); +String getPublicSpendKey() => monero.Wallet_publicSpendKey(wptr!); class SyncListener { SyncListener(this.onNewBlock, this.onNewTransaction) @@ -324,11 +163,11 @@ class SyncListener { SyncListener setListeners(void Function(int, int, double) onNewBlock, void Function() onNewTransaction) { final listener = SyncListener(onNewBlock, onNewTransaction); - setListenerNative(); + // setListenerNative(); return listener; } -void onStartup() => onStartupNative(); +void onStartup() {} void _storeSync(Object _) => storeSync(); @@ -361,8 +200,8 @@ Future setupNode( String? password, bool useSSL = false, String? socksProxyAddress, - bool isLightWallet = false}) => - compute, void>(_setupNodeSync, { + bool isLightWallet = false}) async => + _setupNodeSync({ 'address': address, 'login': login , 'password': password, @@ -371,29 +210,22 @@ Future setupNode( 'socksProxyAddress': socksProxyAddress }); -Future store() => compute(_storeSync, 0); +Future store() async => _storeSync(0); -Future isConnected() => compute(_isConnected, 0); +Future isConnected() async => _isConnected(0); -Future getNodeHeight() => compute(_getNodeHeight, 0); +Future getNodeHeight() async => _getNodeHeight(0); -void rescanBlockchainAsync() => rescanBlockchainAsyncNative(); +void rescanBlockchainAsync() => monero.Wallet_rescanBlockchainAsync(wptr!); String getSubaddressLabel(int accountIndex, int addressIndex) { - return convertUTF8ToString(pointer: getSubaddressLabelNative(accountIndex, addressIndex)); + return monero.Wallet_getSubaddressLabel(wptr!, accountIndex: accountIndex, addressIndex: addressIndex); } -Future setTrustedDaemon(bool trusted) async => setTrustedDaemonNative(_boolToInt(trusted)); +Future setTrustedDaemon(bool trusted) async => monero.Wallet_setTrustedDaemon(wptr!, arg: trusted); -Future trustedDaemon() async => trustedDaemonNative() != 0; +Future trustedDaemon() async => monero.Wallet_trustedDaemon(wptr!); String signMessage(String message, {String address = ""}) { - final messagePointer = message.toNativeUtf8(); - final addressPointer = address.toNativeUtf8(); - - final signature = convertUTF8ToString(pointer: signMessageNative(messagePointer, addressPointer)); - calloc.free(messagePointer); - calloc.free(addressPointer); - - return signature; + return monero.Wallet_signMessage(wptr!, message: message, address: address); } diff --git a/cw_monero/lib/api/wallet_manager.dart b/cw_monero/lib/api/wallet_manager.dart index 0aa694e9a..5913b2565 100644 --- a/cw_monero/lib/api/wallet_manager.dart +++ b/cw_monero/lib/api/wallet_manager.dart @@ -1,80 +1,44 @@ -import 'dart:ffi'; -import 'package:cw_monero/api/convert_utf8_to_string.dart'; +import 'package:cw_monero/api/account_list.dart'; import 'package:cw_monero/api/exceptions/wallet_creation_exception.dart'; import 'package:cw_monero/api/exceptions/wallet_opening_exception.dart'; import 'package:cw_monero/api/exceptions/wallet_restore_from_keys_exception.dart'; import 'package:cw_monero/api/exceptions/wallet_restore_from_seed_exception.dart'; -import 'package:cw_monero/api/monero_api.dart'; -import 'package:cw_monero/api/signatures.dart'; -import 'package:cw_monero/api/types.dart'; import 'package:cw_monero/api/wallet.dart'; import 'package:ffi/ffi.dart'; -import 'package:flutter/foundation.dart'; +import 'package:monero/monero.dart' as monero; +import 'dart:ffi'; -final createWalletNative = moneroApi - .lookup>('create_wallet') - .asFunction(); - -final restoreWalletFromSeedNative = moneroApi - .lookup>( - 'restore_wallet_from_seed') - .asFunction(); - -final restoreWalletFromKeysNative = moneroApi - .lookup>( - 'restore_wallet_from_keys') - .asFunction(); - -final restoreWalletFromSpendKeyNative = moneroApi - .lookup>( - 'restore_wallet_from_spend_key') - .asFunction(); - -final isWalletExistNative = moneroApi - .lookup>('is_wallet_exist') - .asFunction(); - -final loadWalletNative = moneroApi - .lookup>('load_wallet') - .asFunction(); - -final errorStringNative = moneroApi - .lookup>('error_string') - .asFunction(); +monero.WalletManager? _wmPtr; +final monero.WalletManager wmPtr = Pointer.fromAddress((() { + try { + monero.printStarts = true; + _wmPtr ??= monero.WalletManagerFactory_getWalletManager(); + print("ptr: $_wmPtr"); + } catch (e) { + print(e); + } + return _wmPtr!.address; +})()); void createWalletSync( {required String path, required String password, required String language, int nettype = 0}) { - final pathPointer = path.toNativeUtf8(); - final passwordPointer = password.toNativeUtf8(); - final languagePointer = language.toNativeUtf8(); - final errorMessagePointer = ''.toNativeUtf8(); - final isWalletCreated = createWalletNative(pathPointer, passwordPointer, - languagePointer, nettype, errorMessagePointer) != - 0; + wptr = monero.WalletManager_createWallet(wmPtr, path: path, password: password, language: language); - calloc.free(pathPointer); - calloc.free(passwordPointer); - calloc.free(languagePointer); - - if (!isWalletCreated) { - throw WalletCreationException( - message: convertUTF8ToString(pointer: errorMessagePointer)); + final status = monero.Wallet_status(wptr!); + if (status != 0) { + throw WalletCreationException(message: monero.Wallet_errorString(wptr!)); } + // is the line below needed? // setupNodeSync(address: "node.moneroworld.com:18089"); } bool isWalletExistSync({required String path}) { - final pathPointer = path.toNativeUtf8(); - final isExist = isWalletExistNative(pathPointer) != 0; - - calloc.free(pathPointer); - - return isExist; + return monero.WalletManager_walletExists(wmPtr, path); } void restoreWalletFromSeedSync( @@ -87,22 +51,20 @@ void restoreWalletFromSeedSync( final passwordPointer = password.toNativeUtf8(); final seedPointer = seed.toNativeUtf8(); final errorMessagePointer = ''.toNativeUtf8(); - final isWalletRestored = restoreWalletFromSeedNative( - pathPointer, - passwordPointer, - seedPointer, - nettype, - restoreHeight, - errorMessagePointer) != - 0; - calloc.free(pathPointer); - calloc.free(passwordPointer); - calloc.free(seedPointer); + wptr = monero.WalletManager_recoveryWallet( + wmPtr, + path: path, + password: password, + mnemonic: seed, + restoreHeight: restoreHeight, + seedOffset: '', + ); + + final status = monero.Wallet_status(wptr!); - if (!isWalletRestored) { - throw WalletRestoreFromSeedException( - message: convertUTF8ToString(pointer: errorMessagePointer)); + if (status != 0) { + throw WalletRestoreFromSeedException(message: monero.Wallet_errorString(wptr!)); } } @@ -122,28 +84,19 @@ void restoreWalletFromKeysSync( final viewKeyPointer = viewKey.toNativeUtf8(); final spendKeyPointer = spendKey.toNativeUtf8(); final errorMessagePointer = ''.toNativeUtf8(); - final isWalletRestored = restoreWalletFromKeysNative( - pathPointer, - passwordPointer, - languagePointer, - addressPointer, - viewKeyPointer, - spendKeyPointer, - nettype, - restoreHeight, - errorMessagePointer) != - 0; - - calloc.free(pathPointer); - calloc.free(passwordPointer); - calloc.free(languagePointer); - calloc.free(addressPointer); - calloc.free(viewKeyPointer); - calloc.free(spendKeyPointer); - - if (!isWalletRestored) { - throw WalletRestoreFromKeysException( - message: convertUTF8ToString(pointer: errorMessagePointer)); + wptr = monero.WalletManager_createWalletFromKeys( + wmPtr, + path: path, + password: password, + restoreHeight: restoreHeight, + addressString: address, + viewKeyString: viewKey, + spendKeyString: spendKey, + ); + + final status = monero.Wallet_status(wptr!); + if (status != 0) { + throw WalletRestoreFromKeysException(message: monero.Wallet_errorString(wptr!)); } } @@ -161,43 +114,40 @@ void restoreWalletFromSpendKeySync( final languagePointer = language.toNativeUtf8(); final spendKeyPointer = spendKey.toNativeUtf8(); final errorMessagePointer = ''.toNativeUtf8(); - final isWalletRestored = restoreWalletFromSpendKeyNative( - pathPointer, - passwordPointer, - seedPointer, - languagePointer, - spendKeyPointer, - nettype, - restoreHeight, - errorMessagePointer) != - 0; - calloc.free(pathPointer); - calloc.free(passwordPointer); - calloc.free(languagePointer); - calloc.free(spendKeyPointer); + wptr = monero.WalletManager_createWalletFromKeys( + wmPtr, + path: path, + password: password, + restoreHeight: restoreHeight, + addressString: '', + spendKeyString: spendKey, + viewKeyString: '', + ); + + final status = monero.Wallet_status(wptr!); + + if (status == 0) { + throw WalletRestoreFromKeysException(message: monero.Wallet_errorString(wptr!)); + } storeSync(); - - if (!isWalletRestored) { - throw WalletRestoreFromKeysException( - message: convertUTF8ToString(pointer: errorMessagePointer)); - } } void loadWallet({ required String path, required String password, int nettype = 0}) { - final pathPointer = path.toNativeUtf8(); - final passwordPointer = password.toNativeUtf8(); - final loaded = loadWalletNative(pathPointer, passwordPointer, nettype) != 0; - calloc.free(pathPointer); - calloc.free(passwordPointer); - - if (!loaded) { - throw WalletOpeningException( - message: convertUTF8ToString(pointer: errorStringNative())); + try { + wptr ??= monero.WalletManager_openWallet(wmPtr, path: path, password: password); + } catch (e) { + print(e); + } + final status = monero.Wallet_status(wptr!); + if (status != 0) { + final err = monero.Wallet_errorString(wptr!); + print(err); + throw WalletOpeningException(message: err); } } @@ -258,20 +208,20 @@ void _restoreFromSpendKey(Map args) { Future _openWallet(Map args) async => loadWallet(path: args['path'] as String, password: args['password'] as String); -bool _isWalletExist(String path) => isWalletExistSync(path: path); +Future _isWalletExist(String path) async => isWalletExistSync(path: path); void openWallet({required String path, required String password, int nettype = 0}) async => loadWallet(path: path, password: password, nettype: nettype); Future openWalletAsync(Map args) async => - compute(_openWallet, args); + _openWallet(args); Future createWallet( {required String path, required String password, required String language, int nettype = 0}) async => - compute(_createWallet, { + _createWallet({ 'path': path, 'password': password, 'language': language, @@ -284,7 +234,7 @@ Future restoreFromSeed( required String seed, int nettype = 0, int restoreHeight = 0}) async => - compute, void>(_restoreFromSeed, { + _restoreFromSeed({ 'path': path, 'password': password, 'seed': seed, @@ -301,7 +251,7 @@ Future restoreFromKeys( required String spendKey, int nettype = 0, int restoreHeight = 0}) async => - compute, void>(_restoreFromKeys, { + _restoreFromKeys({ 'path': path, 'password': password, 'language': language, @@ -320,7 +270,7 @@ Future restoreFromSpendKey( required String spendKey, int nettype = 0, int restoreHeight = 0}) async => - compute, void>(_restoreFromSpendKey, { + _restoreFromSpendKey({ 'path': path, 'password': password, 'seed': seed, @@ -330,4 +280,4 @@ Future restoreFromSpendKey( 'restoreHeight': restoreHeight }); -Future isWalletExist({required String path}) => compute(_isWalletExist, path); +Future isWalletExist({required String path}) => _isWalletExist(path); diff --git a/cw_monero/lib/monero_account_list.dart b/cw_monero/lib/monero_account_list.dart index 2fd11b3ba..29d096efd 100644 --- a/cw_monero/lib/monero_account_list.dart +++ b/cw_monero/lib/monero_account_list.dart @@ -2,7 +2,7 @@ import 'package:cw_core/monero_amount_format.dart'; import 'package:mobx/mobx.dart'; import 'package:cw_core/account.dart'; import 'package:cw_monero/api/account_list.dart' as account_list; -import 'package:cw_monero/api/wallet.dart' as monero_wallet; +import 'package:monero/monero.dart' as monero; part 'monero_account_list.g.dart'; @@ -44,13 +44,12 @@ abstract class MoneroAccountListBase with Store { } List getAll() => account_list.getAllAccount().map((accountRow) { - final accountIndex = accountRow.getId(); - final balance = monero_wallet.getFullBalance(accountIndex: accountIndex); + final balance = monero.SubaddressAccountRow_getUnlockedBalance(accountRow); return Account( - id: accountRow.getId(), - label: accountRow.getLabel(), - balance: moneroAmountToString(amount: balance), + id: monero.SubaddressAccountRow_getRowId(accountRow), + label: monero.SubaddressAccountRow_getLabel(accountRow), + balance: moneroAmountToString(amount: monero.Wallet_amountFromString(balance)), ); }).toList(); diff --git a/cw_monero/lib/monero_subaddress_list.dart b/cw_monero/lib/monero_subaddress_list.dart index dbd1a89ae..2d3ecdb7e 100644 --- a/cw_monero/lib/monero_subaddress_list.dart +++ b/cw_monero/lib/monero_subaddress_list.dart @@ -3,6 +3,7 @@ import 'package:mobx/mobx.dart'; import 'package:cw_monero/api/coins_info.dart'; import 'package:cw_monero/api/subaddress_list.dart' as subaddress_list; import 'package:cw_core/subaddress.dart'; +import 'package:monero/monero.dart' as monero; part 'monero_subaddress_list.g.dart'; @@ -51,18 +52,21 @@ abstract class MoneroSubaddressListBase with Store { } return subaddresses.map((subaddressRow) { + final label = monero.SubaddressRow_getLabel(subaddressRow); + final id = monero.SubaddressRow_getRowId(subaddressRow); + final address = monero.SubaddressRow_getAddress(subaddressRow); final hasDefaultAddressName = - subaddressRow.getLabel().toLowerCase() == 'Primary account'.toLowerCase() || - subaddressRow.getLabel().toLowerCase() == 'Untitled account'.toLowerCase(); - final isPrimaryAddress = subaddressRow.getId() == 0 && hasDefaultAddressName; + label.toLowerCase() == 'Primary account'.toLowerCase() || + label.toLowerCase() == 'Untitled account'.toLowerCase(); + final isPrimaryAddress = id == 0 && hasDefaultAddressName; return Subaddress( - id: subaddressRow.getId(), - address: subaddressRow.getAddress(), + id: id, + address: address, label: isPrimaryAddress ? 'Primary address' : hasDefaultAddressName ? '' - : subaddressRow.getLabel()); + : label); }).toList(); } @@ -121,8 +125,8 @@ abstract class MoneroSubaddressListBase with Store { Future> _getAllUnusedAddresses( {required int accountIndex, required String label}) async { final allAddresses = subaddress_list.getAllSubaddresses(); - - if (allAddresses.isEmpty || _usedAddresses.contains(allAddresses.last.getAddress())) { + final lastAddress = monero.SubaddressRow_getAddress(allAddresses.last); + if (allAddresses.isEmpty || _usedAddresses.contains(lastAddress)) { final isAddressUnused = await _newSubaddress(accountIndex: accountIndex, label: label); if (!isAddressUnused) { return await _getAllUnusedAddresses(accountIndex: accountIndex, label: label); @@ -130,13 +134,18 @@ abstract class MoneroSubaddressListBase with Store { } return allAddresses - .map((subaddressRow) => Subaddress( - id: subaddressRow.getId(), - address: subaddressRow.getAddress(), - label: subaddressRow.getId() == 0 && - subaddressRow.getLabel().toLowerCase() == 'Primary account'.toLowerCase() + .map((subaddressRow) { + final id = monero.SubaddressRow_getRowId(subaddressRow); + final address = monero.SubaddressRow_getAddress(subaddressRow); + final label = monero.SubaddressRow_getLabel(subaddressRow); + return Subaddress( + id: id, + address: address, + label: id == 0 && + label.toLowerCase() == 'Primary account'.toLowerCase() ? 'Primary address' - : subaddressRow.getLabel())) + : label); + }) .toList(); } @@ -145,7 +154,10 @@ abstract class MoneroSubaddressListBase with Store { return subaddress_list .getAllSubaddresses() - .where((subaddressRow) => !_usedAddresses.contains(subaddressRow.getAddress())) + .where((subaddressRow) { + final address = monero.SubaddressRow_getAddress(subaddressRow); + return !_usedAddresses.contains(address); + }) .isNotEmpty; } } diff --git a/cw_monero/lib/monero_wallet.dart b/cw_monero/lib/monero_wallet.dart index d00a54c8f..e2e7196ba 100644 --- a/cw_monero/lib/monero_wallet.dart +++ b/cw_monero/lib/monero_wallet.dart @@ -12,6 +12,7 @@ import 'package:cw_core/node.dart'; import 'package:cw_core/pathForWallet.dart'; import 'package:cw_core/pending_transaction.dart'; import 'package:cw_core/sync_status.dart'; +import 'package:cw_core/transaction_direction.dart'; import 'package:cw_core/transaction_priority.dart'; import 'package:cw_core/unspent_coins_info.dart'; import 'package:cw_core/wallet_base.dart'; @@ -32,6 +33,7 @@ import 'package:cw_monero/pending_monero_transaction.dart'; import 'package:flutter/foundation.dart'; import 'package:hive/hive.dart'; import 'package:mobx/mobx.dart'; +import 'package:monero/monero.dart' as monero; part 'monero_wallet.g.dart'; @@ -417,10 +419,18 @@ abstract class MoneroWalletBase final coinCount = countOfCoins(); for (var i = 0; i < coinCount; i++) { final coin = getCoin(i); - if (coin.spent == 0) { - final unspent = MoneroUnspent.fromCoinsInfoRow(coin); + final coinSpent = monero.CoinsInfo_spent(coin); + if (coinSpent == 0) { + final unspent = MoneroUnspent( + monero.CoinsInfo_address(coin), + monero.CoinsInfo_hash(coin), + monero.CoinsInfo_keyImage(coin), + monero.CoinsInfo_amount(coin), + monero.CoinsInfo_frozen(coin), + monero.CoinsInfo_unlocked(coin), + ); if (unspent.hash.isNotEmpty) { - unspent.isChange = transaction_history.getTransaction(unspent.hash).direction == 1; + unspent.isChange = transaction_history.getTransaction(unspent.hash) == 1; } unspentCoins.add(unspent); } @@ -541,7 +551,17 @@ abstract class MoneroWalletBase List _getAllTransactionsOfAccount(int? accountIndex) => transaction_history .getAllTransactions() - .map((row) => MoneroTransactionInfo.fromRow(row)) + .map((row) => MoneroTransactionInfo( + row.hash, + row.blockheight, + row.isSpend ? TransactionDirection.outgoing : TransactionDirection.incoming, + row.timeStamp, + row.isPending, + row.amount, + row.accountIndex, + 0, + row.fee, + row.confirmations)) .where((element) => element.accountIndex == (accountIndex ?? 0)) .toList(); diff --git a/cw_monero/pubspec.lock b/cw_monero/pubspec.lock index 0f8f2c90e..0faf7dab8 100644 --- a/cw_monero/pubspec.lock +++ b/cw_monero/pubspec.lock @@ -204,10 +204,10 @@ packages: dependency: "direct main" description: name: ffi - sha256: a38574032c5f1dd06c4aee541789906c12ccaab8ba01446e800d9c5b79c4a978 + sha256: "7bf0adc28a23d395f19f3f1eb21dd7cfd1dd9f8e1c50051c069122e6853bc878" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.1.0" file: dependency: transitive description: @@ -410,6 +410,15 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.1" + monero: + dependency: "direct main" + description: + path: "." + ref: master + resolved-ref: "08c5a32cbcf1f04dbae5826c83abda8fb0dbdcce" + url: "https://git.mrcyjanek.net/mrcyjanek/monero.dart" + source: git + version: "0.0.0" package_config: dependency: transitive description: diff --git a/cw_monero/pubspec.yaml b/cw_monero/pubspec.yaml index a6fe7f967..e94b65067 100644 --- a/cw_monero/pubspec.yaml +++ b/cw_monero/pubspec.yaml @@ -22,6 +22,10 @@ dependencies: polyseed: ^0.0.2 cw_core: path: ../cw_core + monero: + git: + url: https://git.mrcyjanek.net/mrcyjanek/monero.dart + ref: master dev_dependencies: flutter_test: diff --git a/pubspec_base.yaml b/pubspec_base.yaml index 3ec3e7978..0f395007d 100644 --- a/pubspec_base.yaml +++ b/pubspec_base.yaml @@ -111,7 +111,10 @@ dependencies: git: url: https://github.com/cake-tech/bitcoin_base.git ref: cake-update-v2 - + monero: + git: + url: https://git.mrcyjanek.net/mrcyjanek/monero.dart + ref: master dev_dependencies: flutter_test: sdk: flutter