import 'dart:convert'; import 'dart:ffi'; import 'dart:io'; import 'package:cw_core/node.dart'; import 'package:ffi/ffi.dart'; import 'package:flutter/foundation.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/exceptions/wallet_opening_exception.dart'; import 'package:cw_monero/api/exceptions/wallet_creation_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:http/http.dart' as http; import 'package:http/io_client.dart' as ioc; 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 isWalletExistNative = moneroApi .lookup>('is_wallet_exist') .asFunction(); final loadWalletNative = moneroApi.lookup>('load_wallet').asFunction(); final errorStringNative = moneroApi.lookup>('error_string').asFunction(); 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; calloc.free(pathPointer); calloc.free(passwordPointer); calloc.free(languagePointer); if (!isWalletCreated) { throw WalletCreationException(message: convertUTF8ToString(pointer: errorMessagePointer)); } // 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; } void restoreWalletFromSeedSync( {required String path, required String password, required String seed, int nettype = 0, int restoreHeight = 0}) { final pathPointer = path.toNativeUtf8(); 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); if (!isWalletRestored) { throw WalletRestoreFromSeedException( message: convertUTF8ToString(pointer: errorMessagePointer)); } } void restoreWalletFromKeysSync( {required String path, required String password, required String language, required String address, required String viewKey, required String spendKey, int nettype = 0, int restoreHeight = 0}) { final pathPointer = path.toNativeUtf8(); final passwordPointer = password.toNativeUtf8(); final languagePointer = language.toNativeUtf8(); final addressPointer = address.toNativeUtf8(); 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)); } } 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())); } } void _createWallet(Map args) { final path = args['path'] as String; final password = args['password'] as String; final language = args['language'] as String; createWalletSync(path: path, password: password, language: language); } void _restoreFromSeed(Map args) { final path = args['path'] as String; final password = args['password'] as String; final seed = args['seed'] as String; final restoreHeight = args['restoreHeight'] as int; restoreWalletFromSeedSync( path: path, password: password, seed: seed, restoreHeight: restoreHeight); } void _restoreFromKeys(Map args) { final path = args['path'] as String; final password = args['password'] as String; final language = args['language'] as String; final restoreHeight = args['restoreHeight'] as int; final address = args['address'] as String; final viewKey = args['viewKey'] as String; final spendKey = args['spendKey'] as String; restoreWalletFromKeysSync( path: path, password: password, language: language, restoreHeight: restoreHeight, address: address, viewKey: viewKey, spendKey: spendKey); } Future _openWallet(Map args) async => loadWallet(path: args['path'] as String, password: args['password'] as String); bool _isWalletExist(String path) => 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); Future createWallet( {required String path, required String password, required String language, int nettype = 0}) async => compute(_createWallet, {'path': path, 'password': password, 'language': language, 'nettype': nettype}); Future restoreFromSeed( {required String path, required String password, required String seed, int nettype = 0, int restoreHeight = 0}) async => compute, void>(_restoreFromSeed, { 'path': path, 'password': password, 'seed': seed, 'nettype': nettype, 'restoreHeight': restoreHeight }); Future restoreFromKeys( {required String path, required String password, required String language, required String address, required String viewKey, required String spendKey, int nettype = 0, int restoreHeight = 0}) async => compute, void>(_restoreFromKeys, { 'path': path, 'password': password, 'language': language, 'address': address, 'viewKey': viewKey, 'spendKey': spendKey, 'nettype': nettype, 'restoreHeight': restoreHeight }); Future sweepFundsToNewWallet({ required Node node, required String address, required String paymentId, List subaddressIndices = const [], int accountIndex = 0, int priority = 0, int ringSize = 0, int outputs = 0, int unlockTime = 1, bool getTxKeys = false, bool doNotRelay = false, bool getTxHex = false, bool getTxMetadata = false, int belowAmount = 0, }) async { final uri = Uri.http(node.uriRaw, ''); final path = '/json_rpc'; final rpcUri = Uri.https(uri.authority, path); final realm = 'monero-rpc'; final body = { 'method': 'sweep_all', 'params': { 'address': address, 'account_index': accountIndex, 'subaddr_indices': subaddressIndices, 'priority': priority, 'ring_size': ringSize, 'outputs': outputs, 'unlock_time': unlockTime, 'payment_id': paymentId, 'get_tx_keys': getTxKeys, 'below_amount': belowAmount, 'do_not_relay': doNotRelay, 'get_tx_hex': getTxHex, 'get_tx_metadata': getTxMetadata, }, 'jsonrpc': '2.0', 'id': '0' }; try { final authenticatingClient = HttpClient(); authenticatingClient.addCredentials( rpcUri, realm, HttpClientDigestCredentials(node.login ?? '', node.password ?? ''), ); final http.Client client = ioc.IOClient(authenticatingClient); final response = await client.post( rpcUri, headers: {'Content-Type': 'application/json'}, body: json.encode(body), ); client.close(); final resBody = json.decode(response.body) as Map; return !(resBody['result']['offline'] as bool); } catch (_) { return false; } } Future isWalletExist({required String path}) => compute(_isWalletExist, path);