import 'dart:async'; import 'dart:ffi'; import 'package:ffi/ffi.dart'; import 'package:cw_monero/convert_utf8_to_string.dart'; import 'package:cw_monero/signatures.dart'; import 'package:cw_monero/types.dart'; import 'package:cw_monero/monero_api.dart'; import 'package:cw_monero/exceptions/setup_wallet_exception.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; int _boolToInt(bool value) => value ? 1 : 0; final moneroAPIChannel = const MethodChannel('cw_monero'); final getFileNameNative = moneroApi .lookup>('get_filename') .asFunction(); final getSeedNative = moneroApi.lookup>('seed').asFunction(); final getAddressNative = moneroApi .lookup>('get_address') .asFunction(); final getFullBalanceNative = moneroApi .lookup>('get_full_balance') .asFunction(); final getUnlockedBalanceNative = moneroApi .lookup>('get_unlocked_balance') .asFunction(); final getCurrentHeightNative = moneroApi .lookup>('get_current_height') .asFunction(); final getNodeHeightNative = moneroApi .lookup>('get_node_height') .asFunction(); final isConnectedNative = moneroApi .lookup>('is_connected') .asFunction(); final setupNodeNative = moneroApi .lookup>('setup_node') .asFunction(); final startRefreshNative = moneroApi .lookup>('start_refresh') .asFunction(); 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 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(); 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 setupNodeSync( {String address, String login, String password, bool useSSL = false, bool isLightWallet = false}) { final addressPointer = Utf8.toUtf8(address); Pointer loginPointer; Pointer passwordPointer; if (login != null) { loginPointer = Utf8.toUtf8(login); } if (password != null) { passwordPointer = Utf8.toUtf8(password); } final errorMessagePointer = allocate(); final isSetupNode = setupNodeNative( addressPointer, loginPointer, passwordPointer, _boolToInt(useSSL), _boolToInt(isLightWallet), errorMessagePointer) != 0; free(addressPointer); free(loginPointer); free(passwordPointer); if (!isSetupNode) { throw SetupWalletException( message: convertUTF8ToString(pointer: errorMessagePointer)); } return isSetupNode; } void startRefreshSync() => startRefreshNative(); Future connectToNode() async => connecToNodeNative() != 0; void setRefreshFromBlockHeight({int height}) => setRefreshFromBlockHeightNative(height); void setRecoveringFromSeed({bool isRecovery}) => setRecoveringFromSeedNative(_boolToInt(isRecovery)); void storeSync() { final pathPointer = Utf8.toUtf8(''); storeNative(pathPointer); free(pathPointer); } void closeCurrentWallet() => closeCurrentWalletNative(); String getSecretViewKey() => convertUTF8ToString(pointer: getSecretViewKeyNative()); String getPublicViewKey() => convertUTF8ToString(pointer: getPublicViewKeyNative()); String getSecretSpendKey() => convertUTF8ToString(pointer: getSecretSpendKeyNative()); String getPublicSpendKey() => convertUTF8ToString(pointer: getPublicSpendKeyNative()); Timer _updateSyncInfoTimer; int _lastKnownBlockHeight = 0; void setListeners(Future Function(int) onNewBlock, Future Function() onNeedToRefresh, Future Function() onNewTransaction) { if (_updateSyncInfoTimer != null) { _updateSyncInfoTimer.cancel(); } _updateSyncInfoTimer = Timer.periodic(Duration(milliseconds: 200), (_) async { final syncHeight = getSyncingHeight(); final needToRefresh = isNeededToRefresh(); final newTransactionExist = isNewTransactionExist(); if (_lastKnownBlockHeight != syncHeight && syncHeight != null) { _lastKnownBlockHeight = syncHeight; await onNewBlock(syncHeight); } if (newTransactionExist && onNewTransaction != null) { await onNewTransaction(); } if (needToRefresh && onNeedToRefresh != null) { await onNeedToRefresh(); } }); setListenerNative(); } void closeListeners() { if (_updateSyncInfoTimer != null) { _updateSyncInfoTimer.cancel(); } } void onStartup() => onStartupNative(); void _storeSync(Object _) => storeSync(); bool _setupNodeSync(Map args) { final address = args['address'] as String; final login = (args['login'] ?? '') as String; final password = (args['password'] ?? '') as String; final useSSL = args['useSSL'] as bool; final isLightWallet = args['isLightWallet'] as bool; return setupNodeSync( address: address, login: login, password: password, useSSL: useSSL, isLightWallet: isLightWallet); } bool _isConnected(Object _) => isConnectedSync(); int _getNodeHeight(Object _) => getNodeHeightSync(); void startRefresh() => startRefreshSync(); Future setupNode( {String address, String login, String password, bool useSSL = false, bool isLightWallet = false}) => compute(_setupNodeSync, { 'address': address, 'login': login, 'password': password, 'useSSL': useSSL, 'isLightWallet': isLightWallet }); Future store() => compute(_storeSync, 0); Future isConnected() => compute(_isConnected, 0); Future getNodeHeight() => compute(_getNodeHeight, 0); void rescanBlockchainAsync() => rescanBlockchainAsyncNative();