import 'dart:io'; const bitcoinOutputPath = 'lib/bitcoin/bitcoin.dart'; const moneroOutputPath = 'lib/monero/monero.dart'; const havenOutputPath = 'lib/haven/haven.dart'; const ethereumOutputPath = 'lib/ethereum/ethereum.dart'; const walletTypesPath = 'lib/wallet_types.g.dart'; const pubspecDefaultPath = 'pubspec_default.yaml'; const pubspecOutputPath = 'pubspec.yaml'; Future main(List args) async { const prefix = '--'; final hasBitcoin = args.contains('${prefix}bitcoin'); final hasMonero = args.contains('${prefix}monero'); final hasHaven = args.contains('${prefix}haven'); final hasEthereum = args.contains('${prefix}ethereum'); await generateBitcoin(hasBitcoin); await generateMonero(hasMonero); await generateHaven(hasHaven); await generateEthereum(hasEthereum); await generatePubspec(hasMonero: hasMonero, hasBitcoin: hasBitcoin, hasHaven: hasHaven, hasEthereum: hasEthereum); await generateWalletTypes(hasMonero: hasMonero, hasBitcoin: hasBitcoin, hasHaven: hasHaven, hasEthereum: hasEthereum); } Future generateBitcoin(bool hasImplementation) async { final outputFile = File(bitcoinOutputPath); const bitcoinCommonHeaders = """ import 'package:cw_core/wallet_credentials.dart'; import 'package:cw_core/wallet_info.dart'; import 'package:cw_core/transaction_priority.dart'; import 'package:cw_core/output_info.dart'; import 'package:cw_core/unspent_coins_info.dart'; import 'package:cw_core/wallet_service.dart'; import 'package:cake_wallet/view_model/send/output.dart'; import 'package:hive/hive.dart';"""; const bitcoinCWHeaders = """ import 'package:cw_bitcoin/electrum_wallet.dart'; import 'package:cw_bitcoin/bitcoin_unspent.dart'; import 'package:cw_bitcoin/bitcoin_mnemonic.dart'; import 'package:cw_bitcoin/bitcoin_transaction_priority.dart'; import 'package:cw_bitcoin/bitcoin_wallet.dart'; import 'package:cw_bitcoin/bitcoin_wallet_service.dart'; import 'package:cw_bitcoin/bitcoin_wallet_creation_credentials.dart'; import 'package:cw_bitcoin/bitcoin_amount_format.dart'; import 'package:cw_bitcoin/bitcoin_address_record.dart'; import 'package:cw_bitcoin/bitcoin_transaction_credentials.dart'; import 'package:cw_bitcoin/litecoin_wallet_service.dart'; """; const bitcoinCwPart = "part 'cw_bitcoin.dart';"; const bitcoinContent = """ class Unspent { Unspent(this.address, this.hash, this.value, this.vout) : isSending = true, isFrozen = false, note = ''; final String address; final String hash; final int value; final int vout; bool isSending; bool isFrozen; String note; bool get isP2wpkh => address.startsWith('bc') || address.startsWith('ltc'); } abstract class Bitcoin { TransactionPriority getMediumTransactionPriority(); WalletCredentials createBitcoinRestoreWalletFromSeedCredentials({required String name, required String mnemonic, required String password}); WalletCredentials createBitcoinRestoreWalletFromWIFCredentials({required String name, required String password, required String wif, WalletInfo? walletInfo}); WalletCredentials createBitcoinNewWalletCredentials({required String name, WalletInfo? walletInfo}); List getWordList(); Map getWalletKeys(Object wallet); List getTransactionPriorities(); List getLitecoinTransactionPriorities(); TransactionPriority deserializeBitcoinTransactionPriority(int raw); TransactionPriority deserializeLitecoinTransactionPriority(int raw); int getFeeRate(Object wallet, TransactionPriority priority); Future generateNewAddress(Object wallet); Object createBitcoinTransactionCredentials(List outputs, {required TransactionPriority priority, int? feeRate}); Object createBitcoinTransactionCredentialsRaw(List outputs, {TransactionPriority? priority, required int feeRate}); List getAddresses(Object wallet); String getAddress(Object wallet); String formatterBitcoinAmountToString({required int amount}); double formatterBitcoinAmountToDouble({required int amount}); int formatterStringDoubleToBitcoinAmount(String amount); String bitcoinTransactionPriorityWithLabel(TransactionPriority priority, int rate); List getUnspents(Object wallet); void updateUnspents(Object wallet); WalletService createBitcoinWalletService(Box walletInfoSource, Box unspentCoinSource); WalletService createLitecoinWalletService(Box walletInfoSource, Box unspentCoinSource); TransactionPriority getBitcoinTransactionPriorityMedium(); TransactionPriority getLitecoinTransactionPriorityMedium(); TransactionPriority getBitcoinTransactionPrioritySlow(); TransactionPriority getLitecoinTransactionPrioritySlow(); } """; const bitcoinEmptyDefinition = 'Bitcoin? bitcoin;\n'; const bitcoinCWDefinition = 'Bitcoin? bitcoin = CWBitcoin();\n'; final output = '$bitcoinCommonHeaders\n' + (hasImplementation ? '$bitcoinCWHeaders\n' : '\n') + (hasImplementation ? '$bitcoinCwPart\n\n' : '\n') + (hasImplementation ? bitcoinCWDefinition : bitcoinEmptyDefinition) + '\n' + bitcoinContent; if (outputFile.existsSync()) { await outputFile.delete(); } await outputFile.writeAsString(output); } Future generateMonero(bool hasImplementation) async { final outputFile = File(moneroOutputPath); const moneroCommonHeaders = """ import 'package:mobx/mobx.dart'; import 'package:flutter/foundation.dart'; import 'package:cw_core/wallet_credentials.dart'; import 'package:cw_core/wallet_info.dart'; import 'package:cw_core/transaction_priority.dart'; import 'package:cw_core/transaction_history.dart'; import 'package:cw_core/transaction_info.dart'; import 'package:cw_core/balance.dart'; import 'package:cw_core/output_info.dart'; import 'package:cake_wallet/view_model/send/output.dart'; import 'package:cw_core/wallet_service.dart'; import 'package:hive/hive.dart';"""; const moneroCWHeaders = """ import 'package:cw_core/get_height_by_date.dart'; import 'package:cw_core/monero_amount_format.dart'; import 'package:cw_core/monero_transaction_priority.dart'; import 'package:cw_monero/monero_wallet_service.dart'; import 'package:cw_monero/monero_wallet.dart'; import 'package:cw_monero/monero_transaction_info.dart'; import 'package:cw_monero/monero_transaction_history.dart'; import 'package:cw_monero/monero_transaction_creation_credentials.dart'; import 'package:cw_core/account.dart' as monero_account; import 'package:cw_monero/api/wallet.dart' as monero_wallet_api; import 'package:cw_monero/mnemonics/english.dart'; import 'package:cw_monero/mnemonics/chinese_simplified.dart'; import 'package:cw_monero/mnemonics/dutch.dart'; import 'package:cw_monero/mnemonics/german.dart'; import 'package:cw_monero/mnemonics/japanese.dart'; import 'package:cw_monero/mnemonics/russian.dart'; import 'package:cw_monero/mnemonics/spanish.dart'; import 'package:cw_monero/mnemonics/portuguese.dart'; import 'package:cw_monero/mnemonics/french.dart'; import 'package:cw_monero/mnemonics/italian.dart'; import 'package:cw_monero/pending_monero_transaction.dart'; """; const moneroCwPart = "part 'cw_monero.dart';"; const moneroContent = """ class Account { Account({required this.id, required this.label, this.balance}); final int id; final String label; final String? balance; } class Subaddress { Subaddress({ required this.id, required this.label, required this.address}); final int id; final String label; final String address; } class MoneroBalance extends Balance { MoneroBalance({required this.fullBalance, required this.unlockedBalance}) : formattedFullBalance = monero!.formatterMoneroAmountToString(amount: fullBalance), formattedUnlockedBalance = monero!.formatterMoneroAmountToString(amount: unlockedBalance), super(unlockedBalance, fullBalance); MoneroBalance.fromString( {required this.formattedFullBalance, required this.formattedUnlockedBalance}) : fullBalance = monero!.formatterMoneroParseAmount(amount: formattedFullBalance), unlockedBalance = monero!.formatterMoneroParseAmount(amount: formattedUnlockedBalance), super(monero!.formatterMoneroParseAmount(amount: formattedUnlockedBalance), monero!.formatterMoneroParseAmount(amount: formattedFullBalance)); final int fullBalance; final int unlockedBalance; final String formattedFullBalance; final String formattedUnlockedBalance; @override String get formattedAvailableBalance => formattedUnlockedBalance; @override String get formattedAdditionalBalance => formattedFullBalance; } abstract class MoneroWalletDetails { @observable late Account account; @observable late MoneroBalance balance; } abstract class Monero { MoneroAccountList getAccountList(Object wallet); MoneroSubaddressList getSubaddressList(Object wallet); TransactionHistoryBase getTransactionHistory(Object wallet); MoneroWalletDetails getMoneroWalletDetails(Object wallet); String getTransactionAddress(Object wallet, int accountIndex, int addressIndex); String getSubaddressLabel(Object wallet, int accountIndex, int addressIndex); int getHeigthByDate({required DateTime date}); TransactionPriority getDefaultTransactionPriority(); TransactionPriority getMoneroTransactionPrioritySlow(); TransactionPriority getMoneroTransactionPriorityAutomatic(); TransactionPriority deserializeMoneroTransactionPriority({required int raw}); List getTransactionPriorities(); List getMoneroWordList(String language); WalletCredentials createMoneroRestoreWalletFromKeysCredentials({ required String name, required String spendKey, required String viewKey, required String address, required String password, required String language, required int height}); WalletCredentials createMoneroRestoreWalletFromSeedCredentials({required String name, required String password, required int height, required String mnemonic}); WalletCredentials createMoneroNewWalletCredentials({required String name, required String language, String password,}); Map getKeys(Object wallet); Object createMoneroTransactionCreationCredentials({required List outputs, required TransactionPriority priority}); Object createMoneroTransactionCreationCredentialsRaw({required List outputs, required TransactionPriority priority}); String formatterMoneroAmountToString({required int amount}); double formatterMoneroAmountToDouble({required int amount}); int formatterMoneroParseAmount({required String amount}); Account getCurrentAccount(Object wallet); void setCurrentAccount(Object wallet, int id, String label, String? balance); void onStartup(); int getTransactionInfoAccountId(TransactionInfo tx); WalletService createMoneroWalletService(Box walletInfoSource); Map pendingTransactionInfo(Object transaction); } abstract class MoneroSubaddressList { ObservableList get subaddresses; void update(Object wallet, {required int accountIndex}); void refresh(Object wallet, {required int accountIndex}); List getAll(Object wallet); Future addSubaddress(Object wallet, {required int accountIndex, required String label}); Future setLabelSubaddress(Object wallet, {required int accountIndex, required int addressIndex, required String label}); } abstract class MoneroAccountList { ObservableList get accounts; void update(Object wallet); void refresh(Object wallet); List getAll(Object wallet); Future addAccount(Object wallet, {required String label}); Future setLabelAccount(Object wallet, {required int accountIndex, required String label}); } """; const moneroEmptyDefinition = 'Monero? monero;\n'; const moneroCWDefinition = 'Monero? monero = CWMonero();\n'; final output = '$moneroCommonHeaders\n' + (hasImplementation ? '$moneroCWHeaders\n' : '\n') + (hasImplementation ? '$moneroCwPart\n\n' : '\n') + (hasImplementation ? moneroCWDefinition : moneroEmptyDefinition) + '\n' + moneroContent; if (outputFile.existsSync()) { await outputFile.delete(); } await outputFile.writeAsString(output); } Future generateHaven(bool hasImplementation) async { final outputFile = File(havenOutputPath); const havenCommonHeaders = """ import 'package:mobx/mobx.dart'; import 'package:flutter/foundation.dart'; import 'package:cw_core/wallet_credentials.dart'; import 'package:cw_core/wallet_info.dart'; import 'package:cw_core/transaction_priority.dart'; import 'package:cw_core/transaction_history.dart'; import 'package:cw_core/transaction_info.dart'; import 'package:cw_core/balance.dart'; import 'package:cw_core/output_info.dart'; import 'package:cake_wallet/view_model/send/output.dart'; import 'package:cw_core/wallet_service.dart'; import 'package:hive/hive.dart'; import 'package:cw_core/crypto_currency.dart';"""; const havenCWHeaders = """ import 'package:cw_core/get_height_by_date.dart'; import 'package:cw_core/monero_amount_format.dart'; import 'package:cw_core/monero_transaction_priority.dart'; import 'package:cw_haven/haven_wallet_service.dart'; import 'package:cw_haven/haven_wallet.dart'; import 'package:cw_haven/haven_transaction_info.dart'; import 'package:cw_haven/haven_transaction_history.dart'; import 'package:cw_core/account.dart' as monero_account; import 'package:cw_haven/api/wallet.dart' as monero_wallet_api; import 'package:cw_haven/mnemonics/english.dart'; import 'package:cw_haven/mnemonics/chinese_simplified.dart'; import 'package:cw_haven/mnemonics/dutch.dart'; import 'package:cw_haven/mnemonics/german.dart'; import 'package:cw_haven/mnemonics/japanese.dart'; import 'package:cw_haven/mnemonics/russian.dart'; import 'package:cw_haven/mnemonics/spanish.dart'; import 'package:cw_haven/mnemonics/portuguese.dart'; import 'package:cw_haven/mnemonics/french.dart'; import 'package:cw_haven/mnemonics/italian.dart'; import 'package:cw_haven/haven_transaction_creation_credentials.dart'; import 'package:cw_haven/api/balance_list.dart'; """; const havenCwPart = "part 'cw_haven.dart';"; const havenContent = """ class Account { Account({required this.id, required this.label}); final int id; final String label; } class Subaddress { Subaddress({ required this.id, required this.label, required this.address}); final int id; final String label; final String address; } class HavenBalance extends Balance { HavenBalance({required this.fullBalance, required this.unlockedBalance}) : formattedFullBalance = haven!.formatterMoneroAmountToString(amount: fullBalance), formattedUnlockedBalance = haven!.formatterMoneroAmountToString(amount: unlockedBalance), super(unlockedBalance, fullBalance); HavenBalance.fromString( {required this.formattedFullBalance, required this.formattedUnlockedBalance}) : fullBalance = haven!.formatterMoneroParseAmount(amount: formattedFullBalance), unlockedBalance = haven!.formatterMoneroParseAmount(amount: formattedUnlockedBalance), super(haven!.formatterMoneroParseAmount(amount: formattedUnlockedBalance), haven!.formatterMoneroParseAmount(amount: formattedFullBalance)); final int fullBalance; final int unlockedBalance; final String formattedFullBalance; final String formattedUnlockedBalance; @override String get formattedAvailableBalance => formattedUnlockedBalance; @override String get formattedAdditionalBalance => formattedFullBalance; } class AssetRate { AssetRate(this.asset, this.rate); final String asset; final int rate; } abstract class HavenWalletDetails { // FIX-ME: it's abstruct class @observable late Account account; // FIX-ME: it's abstruct class @observable late HavenBalance balance; } abstract class Haven { HavenAccountList getAccountList(Object wallet); MoneroSubaddressList getSubaddressList(Object wallet); TransactionHistoryBase getTransactionHistory(Object wallet); HavenWalletDetails getMoneroWalletDetails(Object wallet); String getTransactionAddress(Object wallet, int accountIndex, int addressIndex); int getHeightByDate({required DateTime date}); Future getCurrentHeight(); TransactionPriority getDefaultTransactionPriority(); TransactionPriority deserializeMoneroTransactionPriority({required int raw}); List getTransactionPriorities(); List getMoneroWordList(String language); WalletCredentials createHavenRestoreWalletFromKeysCredentials({ required String name, required String spendKey, required String viewKey, required String address, required String password, required String language, required int height}); WalletCredentials createHavenRestoreWalletFromSeedCredentials({required String name, required String password, required int height, required String mnemonic}); WalletCredentials createHavenNewWalletCredentials({required String name, required String language, String password}); Map getKeys(Object wallet); Object createHavenTransactionCreationCredentials({required List outputs, required TransactionPriority priority, required String assetType}); String formatterMoneroAmountToString({required int amount}); double formatterMoneroAmountToDouble({required int amount}); int formatterMoneroParseAmount({required String amount}); Account getCurrentAccount(Object wallet); void setCurrentAccount(Object wallet, int id, String label); void onStartup(); int getTransactionInfoAccountId(TransactionInfo tx); WalletService createHavenWalletService(Box walletInfoSource); CryptoCurrency assetOfTransaction(TransactionInfo tx); List getAssetRate(); } abstract class MoneroSubaddressList { ObservableList get subaddresses; void update(Object wallet, {required int accountIndex}); void refresh(Object wallet, {required int accountIndex}); List getAll(Object wallet); Future addSubaddress(Object wallet, {required int accountIndex, required String label}); Future setLabelSubaddress(Object wallet, {required int accountIndex, required int addressIndex, required String label}); } abstract class HavenAccountList { ObservableList get accounts; void update(Object wallet); void refresh(Object wallet); List getAll(Object wallet); Future addAccount(Object wallet, {required String label}); Future setLabelAccount(Object wallet, {required int accountIndex, required String label}); } """; const havenEmptyDefinition = 'Haven? haven;\n'; const havenCWDefinition = 'Haven? haven = CWHaven();\n'; final output = '$havenCommonHeaders\n' + (hasImplementation ? '$havenCWHeaders\n' : '\n') + (hasImplementation ? '$havenCwPart\n\n' : '\n') + (hasImplementation ? havenCWDefinition : havenEmptyDefinition) + '\n' + havenContent; if (outputFile.existsSync()) { await outputFile.delete(); } await outputFile.writeAsString(output); } Future generateEthereum(bool hasImplementation) async { final outputFile = File(ethereumOutputPath); const ethereumCommonHeaders = """ """; const ethereumCWHeaders = """ import 'package:cake_wallet/view_model/send/output.dart'; import 'package:cw_core/crypto_amount_format.dart'; import 'package:cw_core/crypto_currency.dart'; import 'package:cw_core/erc20_token.dart'; import 'package:cw_core/output_info.dart'; import 'package:cw_core/transaction_info.dart'; import 'package:cw_core/transaction_priority.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_service.dart'; import 'package:cw_ethereum/ethereum_formatter.dart'; import 'package:cw_ethereum/ethereum_mnemonics.dart'; import 'package:cw_ethereum/ethereum_transaction_credentials.dart'; import 'package:cw_ethereum/ethereum_transaction_info.dart'; import 'package:cw_ethereum/ethereum_wallet.dart'; import 'package:cw_ethereum/ethereum_wallet_creation_credentials.dart'; import 'package:cw_ethereum/ethereum_wallet_service.dart'; import 'package:cw_ethereum/ethereum_transaction_priority.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}); WalletCredentials createEthereumRestoreWalletFromSeedCredentials({required String name, required String mnemonic, required String password}); String getAddress(WalletBase wallet); TransactionPriority getDefaultTransactionPriority(); List getTransactionPriorities(); TransactionPriority deserializeEthereumTransactionPriority(int raw); int getEstimatedFee(Object wallet, TransactionPriority priority); Object createEthereumTransactionCredentials( List outputs, { required TransactionPriority priority, required CryptoCurrency currency, int? feeRate, }); Object createEthereumTransactionCredentialsRaw( List outputs, { TransactionPriority? priority, required CryptoCurrency currency, required int feeRate, }); int formatterEthereumParseAmount(String amount); double formatterEthereumAmountToDouble({required TransactionInfo transaction}); List getERC20Currencies(WalletBase wallet); Future addErc20Token(WalletBase wallet, Erc20Token token); Future deleteErc20Token(WalletBase wallet, Erc20Token token); Future getErc20Token(WalletBase wallet, String contractAddress); } """; const ethereumEmptyDefinition = 'Ethereum? ethereum;\n'; const ethereumCWDefinition = 'Ethereum? ethereum = CWEthereum();\n'; final output = '$ethereumCommonHeaders\n' + (hasImplementation ? '$ethereumCWHeaders\n' : '\n') + (hasImplementation ? '$ethereumCwPart\n\n' : '\n') + (hasImplementation ? ethereumCWDefinition : ethereumEmptyDefinition) + '\n' + ethereumContent; if (outputFile.existsSync()) { await outputFile.delete(); } await outputFile.writeAsString(output); } Future generatePubspec({required bool hasMonero, required bool hasBitcoin, required bool hasHaven, required bool hasEthereum}) async { const cwCore = """ cw_core: path: ./cw_core """; const cwMonero = """ cw_monero: path: ./cw_monero """; const cwBitcoin = """ cw_bitcoin: path: ./cw_bitcoin """; const cwHaven = """ cw_haven: path: ./cw_haven """; const cwSharedExternal = """ cw_shared_external: path: ./cw_shared_external """; const cwEthereum = """ cw_ethereum: path: ./cw_ethereum """; final inputFile = File(pubspecOutputPath); final inputText = await inputFile.readAsString(); final inputLines = inputText.split('\n'); final dependenciesIndex = inputLines.indexWhere((line) => line.toLowerCase() == 'dependencies:'); var output = cwCore; if (hasMonero) { output += '\n$cwMonero\n$cwSharedExternal'; } if (hasBitcoin) { output += '\n$cwBitcoin'; } if (hasHaven && !hasMonero) { output += '\n$cwSharedExternal\n$cwHaven'; } else if (hasHaven) { output += '\n$cwHaven'; } if (hasEthereum) { output += '\n$cwEthereum'; } final outputLines = output.split('\n'); inputLines.insertAll(dependenciesIndex + 1, outputLines); final outputContent = inputLines.join('\n'); final outputFile = File(pubspecOutputPath); if (outputFile.existsSync()) { await outputFile.delete(); } await outputFile.writeAsString(outputContent); } Future generateWalletTypes({required bool hasMonero, required bool hasBitcoin, required bool hasHaven, required bool hasEthereum}) async { final walletTypesFile = File(walletTypesPath); if (walletTypesFile.existsSync()) { await walletTypesFile.delete(); } const outputHeader = "import 'package:cw_core/wallet_type.dart';"; const outputDefinition = 'final availableWalletTypes = ['; var outputContent = outputHeader + '\n\n' + outputDefinition + '\n'; if (hasMonero) { outputContent += '\tWalletType.monero,\n'; } if (hasBitcoin) { outputContent += '\tWalletType.bitcoin,\n'; } if (hasEthereum) { outputContent += '\tWalletType.ethereum,\n'; } if (hasBitcoin) { outputContent += '\tWalletType.litecoin,\n'; } if (hasHaven) { outputContent += '\tWalletType.haven,\n'; } outputContent += '];\n'; await walletTypesFile.writeAsString(outputContent); }