diff --git a/cw_ethereum/lib/ethereum_wallet.dart b/cw_ethereum/lib/ethereum_wallet.dart index 2840dba22..f350c628a 100644 --- a/cw_ethereum/lib/ethereum_wallet.dart +++ b/cw_ethereum/lib/ethereum_wallet.dart @@ -1,7 +1,19 @@ +import 'dart:convert'; + +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/wallet_addresses.dart'; import 'package:cw_core/wallet_base.dart'; +import 'package:cw_core/wallet_info.dart'; import 'package:cw_ethereum/ethereum_balance.dart'; import 'package:cw_ethereum/ethereum_transaction_history.dart'; import 'package:cw_ethereum/ethereum_transaction_info.dart'; +import 'package:cw_ethereum/ethereum_wallet_addresses.dart'; +import 'package:cw_ethereum/file.dart'; import 'package:mobx/mobx.dart'; part 'ethereum_wallet.g.dart'; @@ -11,5 +23,87 @@ class EthereumWallet = EthereumWalletBase with _$EthereumWallet; abstract class EthereumWalletBase extends WalletBase with Store { - EthereumWalletBase(super.walletInfo); + EthereumWalletBase({ + required WalletInfo walletInfo, + required this.mnemonic, + required this.privateKey, + required String password, + }) : syncStatus = NotConnectedSyncStatus(), + _password = password, + walletAddresses = EthereumWalletAddresses(walletInfo), + super(walletInfo) { + this.walletInfo = walletInfo; + transactionHistory = EthereumTransactionHistory(); + } + + final String mnemonic; + final String privateKey; + final String _password; + + @override + SyncStatus syncStatus; + + @override + ObservableMap get balance => throw UnimplementedError(); + + @override + int calculateEstimatedFee(TransactionPriority priority, int? amount) { + throw UnimplementedError(); + } + + @override + Future changePassword(String password) { + throw UnimplementedError(); + } + + @override + void close() {} + + @override + Future connectToNode({required Node node}) { + throw UnimplementedError(); + } + + @override + Future createTransaction(Object credentials) { + throw UnimplementedError(); + } + + @override + Future> fetchTransactions() { + throw UnimplementedError(); + } + + @override + Object get keys => throw UnimplementedError(); + + @override + Future rescan({required int height}) { + throw UnimplementedError(); + } + + @override + Future save() async { + final path = await makePath(); + await write(path: path, password: _password, data: toJSON()); + await transactionHistory.save(); + } + + @override + String get seed => mnemonic; + + @override + Future startSync() { + throw UnimplementedError(); + } + + @override + WalletAddresses walletAddresses; + + Future makePath() async => pathForWallet(name: walletInfo.name, type: walletInfo.type); + + String toJSON() => json.encode({ + 'mnemonic': mnemonic, + // TODO: save other attributes + }); } diff --git a/cw_ethereum/lib/ethereum_wallet_addresses.dart b/cw_ethereum/lib/ethereum_wallet_addresses.dart new file mode 100644 index 000000000..aeeecdfbb --- /dev/null +++ b/cw_ethereum/lib/ethereum_wallet_addresses.dart @@ -0,0 +1,28 @@ +import 'package:cw_core/wallet_addresses.dart'; +import 'package:cw_core/wallet_info.dart'; +import 'package:mobx/mobx.dart'; + +part 'ethereum_wallet_addresses.g.dart'; + +class EthereumWalletAddresses = EthereumWalletAddressesBase with _$EthereumWalletAddresses; + +abstract class EthereumWalletAddressesBase extends WalletAddresses with Store { + EthereumWalletAddressesBase(WalletInfo walletInfo) + : address = '', + super(walletInfo); + + @override + String address; + + @override + Future init() { + // TODO: implement init + throw UnimplementedError(); + } + + @override + Future updateAddressesInBox() { + // TODO: implement updateAddressesInBox + throw UnimplementedError(); + } +} diff --git a/cw_ethereum/lib/ethereum_wallet_service.dart b/cw_ethereum/lib/ethereum_wallet_service.dart index 64a3ce21a..605f992fb 100644 --- a/cw_ethereum/lib/ethereum_wallet_service.dart +++ b/cw_ethereum/lib/ethereum_wallet_service.dart @@ -10,7 +10,10 @@ import 'package:cw_core/wallet_service.dart'; import 'package:cw_core/wallet_type.dart'; import 'package:cw_ethereum/ethereum_wallet.dart'; import 'package:cw_ethereum/ethereum_wallet_creation_credentials.dart'; +import 'package:ed25519_hd_key/ed25519_hd_key.dart'; import 'package:hive/hive.dart'; +import 'package:bip39/bip39.dart' as bip39; +import 'package:hex/hex.dart'; class EthereumWalletService extends WalletService { @@ -20,13 +23,15 @@ class EthereumWalletService extends WalletService create(EthereumNewWalletCredentials credentials) async { - final path = await pathForWallet(name: credentials.name, type: getType()); - await monero_wallet_manager.createWallet( - path: path, - password: credentials.password!, + final mnemonic = bip39.generateMnemonic(); + final privateKey = await getPrivateKey(mnemonic, credentials.password!); + final wallet = EthereumWallet( + walletInfo: credentials.walletInfo!, + mnemonic: mnemonic, + privateKey: privateKey, + password: credentials.password!, ); - final wallet = EthereumWallet(credentials.walletInfo!); - await wallet.init(); + await wallet.save(); return wallet; } @@ -61,4 +66,12 @@ class EthereumWalletService extends WalletService getPrivateKey(String mnemonic, String password) async { + final seed = bip39.mnemonicToSeedHex(mnemonic); + final master = await ED25519_HD_KEY.getMasterKeyFromSeed(HEX.decode(seed), + masterSecret: password); + final privateKey = HEX.encode(master.key); + return privateKey; + } } diff --git a/cw_ethereum/lib/file.dart b/cw_ethereum/lib/file.dart new file mode 100644 index 000000000..8fd236ec3 --- /dev/null +++ b/cw_ethereum/lib/file.dart @@ -0,0 +1,39 @@ +import 'dart:io'; +import 'package:cw_core/key.dart'; +import 'package:encrypt/encrypt.dart' as encrypt; + +Future write( + {required String path, + required String password, + required String data}) async { + final keys = extractKeys(password); + final key = encrypt.Key.fromBase64(keys.first); + final iv = encrypt.IV.fromBase64(keys.last); + final encrypted = await encode(key: key, iv: iv, data: data); + final f = File(path); + f.writeAsStringSync(encrypted); +} + +Future writeData( + {required String path, + required String password, + required String data}) async { + final keys = extractKeys(password); + final key = encrypt.Key.fromBase64(keys.first); + final iv = encrypt.IV.fromBase64(keys.last); + final encrypted = await encode(key: key, iv: iv, data: data); + final f = File(path); + f.writeAsStringSync(encrypted); +} + +Future read({required String path, required String password}) async { + final file = File(path); + + if (!file.existsSync()) { + file.createSync(); + } + + final encrypted = file.readAsStringSync(); + + return decode(password: password, data: encrypted); +} diff --git a/cw_ethereum/pubspec.yaml b/cw_ethereum/pubspec.yaml index 9eb2e5863..b004cd14d 100644 --- a/cw_ethereum/pubspec.yaml +++ b/cw_ethereum/pubspec.yaml @@ -12,14 +12,19 @@ environment: dependencies: flutter: sdk: flutter - web3dart: ^2.5.1 +# web3dart: ^2.4.1 mobx: ^2.0.7+4 + bip39: ^1.0.6 + ed25519_hd_key: ^2.2.0 + hex: ^0.2.0 cw_core: path: ../cw_core dev_dependencies: flutter_test: sdk: flutter + build_runner: ^2.1.11 + mobx_codegen: ^2.0.7 hive_generator: ^1.1.3 # For information on the generic Dart part of this file, see the diff --git a/lib/ethereum/cw_ethereum.dart b/lib/ethereum/cw_ethereum.dart index 24c9a49fc..f2765520a 100644 --- a/lib/ethereum/cw_ethereum.dart +++ b/lib/ethereum/cw_ethereum.dart @@ -2,11 +2,15 @@ part of 'ethereum.dart'; class CWEthereum extends Ethereum { @override - List getEthereumWordList(String language) { - return EthereumMnemonics.englishWordlist; - } + List getEthereumWordList(String language) => EthereumMnemonics.englishWordlist; - WalletService createEthereumWalletService(Box walletInfoSource) { - return EthereumWalletService(walletInfoSource); - } + WalletService createEthereumWalletService(Box walletInfoSource) => + EthereumWalletService(walletInfoSource); + + @override + WalletCredentials createEthereumNewWalletCredentials({ + required String name, + WalletInfo? walletInfo, + }) => + EthereumNewWalletCredentials(name: name, walletInfo: walletInfo); } diff --git a/lib/view_model/wallet_new_vm.dart b/lib/view_model/wallet_new_vm.dart index e505cff77..035349978 100644 --- a/lib/view_model/wallet_new_vm.dart +++ b/lib/view_model/wallet_new_vm.dart @@ -1,3 +1,4 @@ +import 'package:cake_wallet/ethereum/ethereum.dart'; import 'package:flutter/foundation.dart'; import 'package:hive/hive.dart'; import 'package:mobx/mobx.dart'; @@ -41,6 +42,8 @@ abstract class WalletNewVMBase extends WalletCreationVM with Store { case WalletType.haven: return haven!.createHavenNewWalletCredentials( name: name, language: options as String); + case WalletType.ethereum: + return ethereum!.createEthereumNewWalletCredentials(name: name); default: throw Exception('Unexpected type: ${type.toString()}');; } diff --git a/tool/configure.dart b/tool/configure.dart index c390c0f2e..c02edb7cc 100644 --- a/tool/configure.dart +++ b/tool/configure.dart @@ -477,12 +477,20 @@ Future generateEthereum(bool hasImplementation) async { const ethereumCommonHeaders = """ """; const ethereumCWHeaders = """ +import 'package:cw_core/wallet_credentials.dart'; +import 'package:cw_core/wallet_info.dart'; +import 'package:cw_core/wallet_service.dart'; import 'package:cw_ethereum/ethereum_mnemonics.dart'; +import 'package:cw_ethereum/ethereum_wallet_creation_credentials.dart'; +import 'package:cw_ethereum/ethereum_wallet_service.dart'; +import 'package:hive/hive.dart'; """; const ethereumCwPart = "part 'cw_ethereum.dart';"; const ethereumContent = """ abstract class Ethereum { List getEthereumWordList(String language); + WalletService createEthereumWalletService(Box walletInfoSource); + WalletCredentials createEthereumNewWalletCredentials({required String name, WalletInfo? walletInfo}); } """;