import "dart:convert"; import "dart:io"; import 'package:cw_core/encryption_file_utils.dart'; import 'package:cw_core/imported_nft.dart'; import 'package:cw_core/pathForWallet.dart'; import 'package:cw_core/spl_token.dart'; import 'package:cw_core/tron_token.dart'; import 'package:cw_core/utils/print_verbose.dart'; import 'package:cw_core/wallet_keys_file.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_type.dart'; import 'package:path/path.dart' as p; abstract class WalletService { WalletType getType(); Future create(N credentials, {bool? isTestnet}); Future restoreFromHardwareWallet(RFH credentials); Future restoreFromSeed(RFS credentials, {bool? isTestnet}); Future restoreFromKeys(RFK credentials, {bool? isTestnet}); Future openWallet(String name, String password); Future isWalletExit(String name); Future remove(String wallet); Future rename(String currentName, String password, String newName) async { if (currentName == newName) { return; } final currentWalletInfo = await WalletInfo.get(currentName, getType()); if (currentWalletInfo == null) { throw Exception("Wallet not found"); } await copyWalletFilesTo(fromName: currentName, toName: newName, type: getType()); await saveBackup(newName); currentWalletInfo.id = WalletBase.idFor(newName, getType()); currentWalletInfo.name = newName; await currentWalletInfo.save(); await _renameTokenRows(currentName, newName); final oldDir = Directory(p.join(await pathForWalletTypeDir(type: getType()), currentName)); if (oldDir.existsSync()) { try { await oldDir.delete(recursive: true); } catch (e) { printV('rename: failed to delete old wallet dir "$currentName": $e'); } } } Future _renameTokenRows(String currentName, String newName) async { if (getType() == WalletType.solana) { await SPLToken.renameWallet(currentName, newName); await ImportedNFT.renameWallet( currentName, newName, chains: const [ImportedNFT.solanaChain], ); } if (getType() == WalletType.tron) { await TronToken.renameWallet(currentName, newName); } } Future restoreWalletFilesFromBackup(String name) async { final backupWalletDirPath = await pathForWalletDir(name: "$name.backup", type: getType()); final walletDirPath = await pathForWalletDir(name: name, type: getType()); if (File(backupWalletDirPath).existsSync()) { await File(backupWalletDirPath).copy(walletDirPath); } } Future saveBackup(String name) async { final backupWalletDirPath = await pathForWalletDir(name: "$name.backup", type: getType()); final walletDirPath = await pathForWalletDir(name: name, type: getType()); if (File(walletDirPath).existsSync()) { await File(walletDirPath).copy(backupWalletDirPath); } } Future getSeeds(String name, String password, WalletType type) async { try { final encryption = encryptionFileUtilsFor(Platform.isLinux); if (await WalletKeysFile.hasKeysFile(name, type)) { final keysData = await WalletKeysFile.readKeysFile(name, type, password, encryption); return keysData.mnemonic ?? keysData.altMnemonic ?? keysData.privateKey ?? ''; } final path = await pathForWallet(name: name, type: type); final jsonSource = await encryption.read(path: path, password: password); try { final data = json.decode(jsonSource) as Map; return data["mnemonic"] as String? ?? ""; } catch (_) { // if not a valid json return jsonSource.substring(0, 200); } } catch (_) { // if the file couldn't be opened or read return ""; } } /// Check if the Wallet requires a hardware wallet to be connected during /// the opening flow. (Currently only the case for Monero) Future requireHardwareWalletConnection(String name) async => false; }