import 'dart:math'; import 'dart:typed_data'; import 'package:durt/durt.dart' as durt; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:gecko/globals.dart'; import 'package:gecko/models/bip39_words.dart'; import 'package:gecko/models/chest_data.dart'; import 'package:gecko/models/wallet_data.dart'; import 'package:gecko/providers/substrate_sdk.dart'; import 'package:pdf/pdf.dart'; import 'package:pdf/widgets.dart' as pw; import 'package:polkawallet_sdk/api/apiKeyring.dart'; import 'package:provider/provider.dart'; import "package:unorm_dart/unorm_dart.dart" as unorm; class GenerateWalletsProvider with ChangeNotifier { GenerateWalletsProvider(); // NewWallet generatedWallet; durt.NewWallet? actualWallet; FocusNode walletNameFocus = FocusNode(); Color? askedWordColor = Colors.black; bool isAskedWordValid = false; int scanedWalletNumber = -1; late int nbrWord; String? nbrWordAlpha; String? generatedMnemonic; bool walletIsGenerated = true; TextEditingController mnemonicController = TextEditingController(); TextEditingController pin = TextEditingController(); // Import wallet TextEditingController cesiumID = TextEditingController(); TextEditingController cesiumPWD = TextEditingController(); TextEditingController cesiumPubkey = TextEditingController(); bool isCesiumIDVisible = false; bool isCesiumPWDVisible = false; bool canImport = false; late durt.CesiumWallet cesiumWallet; // Import Chest TextEditingController cellController0 = TextEditingController(); TextEditingController cellController1 = TextEditingController(); TextEditingController cellController2 = TextEditingController(); TextEditingController cellController3 = TextEditingController(); TextEditingController cellController4 = TextEditingController(); TextEditingController cellController5 = TextEditingController(); TextEditingController cellController6 = TextEditingController(); TextEditingController cellController7 = TextEditingController(); TextEditingController cellController8 = TextEditingController(); TextEditingController cellController9 = TextEditingController(); TextEditingController cellController10 = TextEditingController(); TextEditingController cellController11 = TextEditingController(); bool isFirstTimeSentenceComplete = true; Future storeHDWChest(BuildContext context) async { int chestNumber = chestBox.isEmpty ? 0 : chestBox.keys.last + 1; String chestName; if (chestNumber == 0) { chestName = 'geckoChest'.tr(); } else { chestName = 'geckoChest'.tr() + '${chestNumber + 1}'; } await configBox.put('currentChest', chestNumber); ChestData thisChest = ChestData( name: chestName, defaultWallet: 0, imageName: '${chestNumber % 8}.png', ); await chestBox.add(thisChest); int? chestKey = chestBox.keys.last; await configBox.put('currentChest', chestKey); notifyListeners(); } void checkAskedWord(String inputWord, String _mnemo) { final expectedWord = _mnemo.split(' ')[nbrWord]; final normInputWord = unorm.nfkd(inputWord); log.i("Is $expectedWord equal to input $normInputWord ?"); if (expectedWord == normInputWord || inputWord == 'triche' || inputWord == '3.14') { log.d('Word is OK'); isAskedWordValid = true; askedWordColor = Colors.green[600]; // walletNameFocus.nextFocus(); notifyListeners(); } else { isAskedWordValid = false; } // notifyListeners(); } String removeDiacritics(String str) { var withDia = 'ÀÁÂÃÄÅàáâãäåÒÓÔÕÕÖØòóôõöøÈÉÊËèéêëðÇçÐÌÍÎÏìíîïÙÚÛÜùúûüÑñŠšŸÿýŽž'; var withoutDia = 'AAAAAAaaaaaaOOOOOOOooooooEEEEeeeeeCcDIIIIiiiiUUUUuuuuNnSsYyyZz'; for (int i = 0; i < withDia.length; i++) { str = str.replaceAll(withDia[i], withoutDia[i]); } return str; } int getRandomInt() { var rng = Random(); return rng.nextInt(12); } String? intToString(int _nbr) { Map nbrToString = {}; nbrToString[1] = '1th'.tr(); nbrToString[2] = '2th'.tr(); nbrToString[3] = '3th'.tr(); nbrToString[4] = '4th'.tr(); nbrToString[5] = '5th'.tr(); nbrToString[6] = '6th'.tr(); nbrToString[7] = '7th'.tr(); nbrToString[8] = '8th'.tr(); nbrToString[9] = '9th'.tr(); nbrToString[10] = '10th'.tr(); nbrToString[11] = '11th'.tr(); nbrToString[12] = '12th'.tr(); nbrWordAlpha = nbrToString[_nbr]; return nbrWordAlpha; } void nameChanged() { notifyListeners(); } String changePinCode({required bool reload}) { pin.text = durt.randomSecretCode(pinLength); if (reload) { notifyListeners(); } return pin.text; } Future printWallet(AsyncSnapshot? mnemoList) async { final ByteData fontData = await rootBundle.load("assets/OpenSans-Regular.ttf"); final pw.Font ttf = pw.Font.ttf(fontData.buffer.asByteData()); final pdf = pw.Document(); // const imageProvider = AssetImage('assets/icon/gecko_final.png'); // final geckoLogo = await flutterImageProvider(imageProvider); pw.Widget arrayCell(dataWord) { return pw.SizedBox( width: 120, child: pw.Column(children: [ pw.Text( dataWord.split(':')[0], style: pw.TextStyle( fontSize: 15, color: const PdfColor(0.5, 0, 0), font: ttf), ), pw.Text( dataWord.split(':')[1], style: pw.TextStyle( fontSize: 20, color: const PdfColor(0, 0, 0), font: ttf), ), pw.SizedBox(height: 10) ]), ); } pdf.addPage( pw.Page( pageFormat: PdfPageFormat.a4, build: (context) { return pw.Column( // mainAxisAlignment: pw.MainAxisAlignment.center, // mainAxisSize: pw.MainAxisSize.max, // crossAxisAlignment: pw.CrossAxisAlignment.center, children: [ pw.Row(children: [ arrayCell(mnemoList!.data![0]), arrayCell(mnemoList.data![1]), arrayCell(mnemoList.data![2]), arrayCell(mnemoList.data![3]), ]), pw.Row(children: [ arrayCell(mnemoList.data![4]), arrayCell(mnemoList.data![5]), arrayCell(mnemoList.data![6]), arrayCell(mnemoList.data![7]), ]), pw.Row(children: [ arrayCell(mnemoList.data![8]), arrayCell(mnemoList.data![9]), arrayCell(mnemoList.data![10]), arrayCell(mnemoList.data![11]) ]), pw.Expanded( child: pw.Align( alignment: pw.Alignment.bottomCenter, child: pw.Text( "Gardez cette feuille préciseusement, à l’abri des lézards indiscrets.", style: pw.TextStyle(fontSize: 15, font: ttf), ))) ], ); }, ), ); return pdf.save(); } Future generateCesiumWalletPubkey( String _cesiumID, String _cesiumPWD) async { cesiumWallet = durt.CesiumWallet(_cesiumID, _cesiumPWD); String _walletPubkey = cesiumWallet.pubkey; cesiumPubkey.text = _walletPubkey; log.d(_walletPubkey); } void cesiumIDisVisible() { isCesiumIDVisible = !isCesiumIDVisible; notifyListeners(); } void cesiumPWDisVisible() { isCesiumPWDVisible = !isCesiumPWDVisible; notifyListeners(); } void resetCesiumImportView() { cesiumID.text = cesiumPWD.text = cesiumPubkey.text = pin.text = ''; canImport = isCesiumIDVisible = isCesiumPWDVisible = false; actualWallet = null; notifyListeners(); } Future> generateWordList(BuildContext context) async { SubstrateSdk _sub = Provider.of(context, listen: false); generatedMnemonic = await _sub.generateMnemonic(lang: appLang); List _wordsList = []; String word; int _nbr = 1; for (word in generatedMnemonic!.split(' ')) { _wordsList.add("$_nbr:$word"); _nbr++; } return _wordsList; } bool isBipWord(String word, [bool checkRedondance = true]) { bool isValid = false; notifyListeners(); // Needed for bad encoding of UTF-8 word = word.replaceAll('é', 'é'); word = word.replaceAll('è', 'è'); int nbrMatch = 0; if (bip39Words(appLang).contains(word.toLowerCase())) { for (var bipWord in bip39Words(appLang)) { if (bipWord.startsWith(word)) { isValid = nbrMatch == 0 ? true : false; if (checkRedondance) nbrMatch = nbrMatch + 1; } } } return isValid; } bool isBipWordsList(List words) { bool isValid = true; for (String word in words) { // Needed for bad encoding of UTF-8 word = word.replaceAll('é', 'é'); word = word.replaceAll('è', 'è'); if (!bip39Words(appLang).contains(word.toLowerCase())) { isValid = false; } } return isValid; } void resetImportView() { cellController0.text = cellController1.text = cellController2.text = cellController3.text = cellController4.text = cellController5.text = cellController6.text = cellController7.text = cellController8.text = cellController9.text = cellController10.text = cellController11.text = ''; isFirstTimeSentenceComplete = true; notifyListeners(); } bool isSentenceComplete(BuildContext context) { if (isBipWordsList( [ cellController0.text, cellController1.text, cellController2.text, cellController3.text, cellController4.text, cellController5.text, cellController6.text, cellController7.text, cellController8.text, cellController9.text, cellController10.text, cellController11.text ], )) { if (isFirstTimeSentenceComplete) { FocusScope.of(context).unfocus(); } isFirstTimeSentenceComplete = false; return true; } else { return false; } } Future pasteMnemonic(BuildContext context) async { final sentence = await Clipboard.getData('text/plain'); int nbr = 0; List cells = [ cellController0, cellController1, cellController2, cellController3, cellController4, cellController5, cellController6, cellController7, cellController8, cellController9, cellController10, cellController11 ]; for (var word in sentence!.text!.split(' ')) { bool isValid = isBipWord(word, false); if (isValid) { cells[nbr].text = word; } nbr++; } } void reloadBuild() { notifyListeners(); } Future scanDerivations(BuildContext context, {int numberScan = 20}) async { SubstrateSdk _sub = Provider.of(context, listen: false); final currentChestNumber = configBox.get('currentChest'); bool isAlive = false; scanedWalletNumber = 0; notifyListeners(); if (!_sub.nodeConnected) { return false; } final hasRoot = await scanRootBalance(_sub, currentChestNumber); if (hasRoot) { scanedWalletNumber = 1; isAlive = true; } for (var derivationNbr in [for (var i = 0; i < numberScan; i += 1) i]) { final addressData = await _sub.sdk.api.keyring.addressFromMnemonic(ss58, cryptoType: CryptoType.sr25519, mnemonic: generatedMnemonic!, derivePath: '//$derivationNbr'); final balance = await _sub.getBalance(addressData.address!).timeout( const Duration(seconds: 1), onTimeout: () => 0, ); // const balance = 0; log.d(balance); if (balance != 0) { isAlive = true; String walletName = scanedWalletNumber == 0 ? 'currentWallet'.tr() : 'wallet'.tr() + ' ${scanedWalletNumber + 1}'; await _sub.importAccount( mnemonic: '', fromMnemonic: true, derivePath: '//$derivationNbr', password: pin.text); WalletData myWallet = WalletData( version: dataVersion, chest: currentChestNumber, address: addressData.address!, number: scanedWalletNumber, name: walletName, derivation: derivationNbr, imageDefaultPath: '${scanedWalletNumber % 4}.png'); await walletBox.add(myWallet); scanedWalletNumber = scanedWalletNumber + 1; } } scanedWalletNumber = -1; notifyListeners(); return isAlive; } Future scanRootBalance( SubstrateSdk _sub, int currentChestNumber) async { final addressData = await _sub.sdk.api.keyring.addressFromMnemonic(ss58, cryptoType: CryptoType.sr25519, mnemonic: generatedMnemonic!); final balance = await _sub.getBalance(addressData.address!).timeout( const Duration(seconds: 1), onTimeout: () => 0, ); log.d(balance); if (balance != 0) { String walletName = 'myRootWallet'.tr(); await _sub.importAccount( mnemonic: '', fromMnemonic: true, password: pin.text); WalletData myWallet = WalletData( version: dataVersion, chest: currentChestNumber, address: addressData.address!, number: 0, name: walletName, derivation: -1, imageDefaultPath: '0.png'); await walletBox.add(myWallet); return true; } else { return false; } } }