diff --git a/lib/main.dart b/lib/main.dart index 9ae14938b89de6e4b5a699c8f115f1a5a3f450c8..586c97192cb81d9c690d92fcfc637f5f081af7cd 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -74,6 +74,9 @@ Future<void> main() async { await _homeProvider.getValidEndpoints(); // await configBox.delete('isCacheChecked'); + if (configBox.get('isCacheChecked') == null) { + configBox.put('isCacheChecked', false); + } // log.d(await configBox.get('endpoint')); HttpOverrides.global = MyHttpOverrides(); diff --git a/lib/providers/generate_wallets.dart b/lib/providers/generate_wallets.dart index 763fc9349e281295cd7414888cea51ebd497bf7d..350d5b3fba496a73aa6200ba9c39daf0de99ae7f 100644 --- a/lib/providers/generate_wallets.dart +++ b/lib/providers/generate_wallets.dart @@ -57,12 +57,7 @@ class GenerateWalletsProvider with ChangeNotifier { Future storeHDWChest( String address, String _name, BuildContext context) async { - int chestNumber = 0; - chestBox.toMap().forEach((key, value) { - if (!value.isCesium!) { - chestNumber++; - } - }); + int chestNumber = chestBox.isEmpty ? 0 : chestBox.keys.last + 1; String chestName; if (chestNumber == 0) { diff --git a/lib/providers/my_wallets.dart b/lib/providers/my_wallets.dart index c058ec43d31db5e204dde23a520e95c0a8e359fa..b48bafbf90d18922f153e9f1542fb8ca05ae6daa 100644 --- a/lib/providers/my_wallets.dart +++ b/lib/providers/my_wallets.dart @@ -30,7 +30,8 @@ class MyWalletsProvider with ChangeNotifier { } } - List<WalletData> readAllWallets(int? _chest) { + List<WalletData> readAllWallets([int? _chest]) { + _chest = _chest ?? configBox.get('currentChest') ?? 0; listWallets.clear(); walletBox.toMap().forEach((key, value) { if (value.chest == _chest) { @@ -108,31 +109,61 @@ class MyWalletsProvider with ChangeNotifier { } } - Future<void> generateNewDerivation(context, String _name) async { + Future<void> generateNewDerivation(context, String _name, + [int? number]) async { + isNewDerivationLoading = true; + notifyListeners(); + + final List idList = getNextWalletNumberAndDerivation(); + int _newWalletNbr = idList[0]; + int _newDerivationNbr = number ?? idList[1]; + + int? _chest = getCurrentChest(); + + SubstrateSdk _sub = Provider.of<SubstrateSdk>(context, listen: false); + + WalletData defaultWallet = getDefaultWallet()!; + + final address = await _sub.derive( + context, defaultWallet.address!, _newDerivationNbr, pinCode); + + WalletData newWallet = WalletData( + version: dataVersion, + chest: _chest, + address: address, + number: _newWalletNbr, + name: _name, + derivation: _newDerivationNbr, + imageDefaultPath: '${_newWalletNbr % 4}.png'); + + await walletBox.add(newWallet); + + isNewDerivationLoading = false; + notifyListeners(); + } + + Future<void> generateRootWallet(context, String _name) async { MyWalletsProvider _myWalletProvider = Provider.of<MyWalletsProvider>(context, listen: false); isNewDerivationLoading = true; notifyListeners(); - int _newDerivationNbr; int _newWalletNbr; int? _chest = getCurrentChest(); List<WalletData> _walletConfig = readAllWallets(_chest); if (_walletConfig.isEmpty) { - _newDerivationNbr = 2; _newWalletNbr = 0; } else { - _newDerivationNbr = _walletConfig.last.derivation! + 2; _newWalletNbr = _walletConfig.last.number! + 1; } SubstrateSdk _sub = Provider.of<SubstrateSdk>(context, listen: false); WalletData defaultWallet = _myWalletProvider.getDefaultWallet()!; - final address = await _sub.derive( - context, defaultWallet.address!, _newDerivationNbr, pinCode); + final address = + await _sub.generateRootKeypair(defaultWallet.address!, pinCode); WalletData newWallet = WalletData( version: dataVersion, @@ -140,7 +171,7 @@ class MyWalletsProvider with ChangeNotifier { address: address, number: _newWalletNbr, name: _name, - derivation: _newDerivationNbr, + derivation: -1, imageDefaultPath: '${_newWalletNbr % 4}.png'); await walletBox.add(newWallet); @@ -149,6 +180,33 @@ class MyWalletsProvider with ChangeNotifier { notifyListeners(); } + List<int> getNextWalletNumberAndDerivation( + {int? chestNumber, bool isOneshoot = false}) { + int _newDerivationNbr = 0; + int _newWalletNbr = 0; + + chestNumber ??= getCurrentChest(); + + List<WalletData> _walletConfig = readAllWallets(chestNumber); + + if (_walletConfig.isEmpty) { + _newDerivationNbr = 2; + } else { + WalletData _lastWallet = _walletConfig.reduce( + (curr, next) => curr.derivation! > next.derivation! ? curr : next); + + if (_lastWallet.derivation == -1) { + _newDerivationNbr = 2; + } else { + _newDerivationNbr = _lastWallet.derivation! + (isOneshoot ? 1 : 2); + } + + _newWalletNbr = _walletConfig.last.number! + 1; + } + + return [_newWalletNbr, _newDerivationNbr]; + } + int lockPin = 0; Future resetPinCode([int minutes = 15]) async { lockPin++; diff --git a/lib/providers/substrate_sdk.dart b/lib/providers/substrate_sdk.dart index 6bee52ca1ce940156037d68d65bafcc89dcea0a0..9863596d223fda74c4d07fe9c39dca517c2f64f1 100644 --- a/lib/providers/substrate_sdk.dart +++ b/lib/providers/substrate_sdk.dart @@ -230,6 +230,8 @@ class SubstrateSdk with ChangeNotifier { Future<bool> checkPassword(String address, String pass) async { final account = getKeypair(address); + // log.d(account.address); + return await sdk.api.keyring.checkPassword(account, pass); } @@ -510,7 +512,23 @@ class SubstrateSdk with ChangeNotifier { generatedMnemonic = seedList[0]; return await importAccount( - fromMnemonic: true, derivePath: '//$number', password: password); + mnemonic: generatedMnemonic, + fromMnemonic: true, + derivePath: '//$number', + password: password); + } + + Future<String> generateRootKeypair(String address, String password) async { + final keypair = getKeypair(address); + + final seedMap = + await keyring.store.getDecryptedSeed(keypair.pubKey, password); + + if (seedMap?['type'] != 'mnemonic') return ''; + final List seedList = seedMap!['seed'].split('//'); + generatedMnemonic = seedList[0]; + + return await importAccount(fromMnemonic: true, password: password); } Future<bool> isMnemonicValid(String mnemonic) async { diff --git a/lib/screens/myWallets/chest_options.dart b/lib/screens/myWallets/chest_options.dart index cffe80019cb99d92ec340d6dd9095b9117744e4b..9cb75b51f36e0dc2e7c72e5e8e6ba2f85cf8e015 100644 --- a/lib/screens/myWallets/chest_options.dart +++ b/lib/screens/myWallets/chest_options.dart @@ -7,6 +7,7 @@ import 'package:gecko/providers/chest_provider.dart'; import 'package:gecko/providers/home.dart'; import 'package:gecko/providers/my_wallets.dart'; import 'package:gecko/screens/myWallets/change_pin.dart'; +import 'package:gecko/screens/myWallets/custom_derivations.dart'; import 'package:gecko/screens/myWallets/show_seed.dart'; import 'package:gecko/screens/myWallets/unlocking_wallet.dart'; import 'package:provider/provider.dart'; @@ -123,7 +124,7 @@ class ChestOptions extends StatelessWidget { child: SizedBox( height: 50, child: Row(children: <Widget>[ - const SizedBox(width: 28), + const SizedBox(width: 26), Image.asset( 'assets/chests/secret_code.png', height: 25, @@ -136,6 +137,35 @@ class ChestOptions extends StatelessWidget { ])), ), SizedBox(height: 10 * ratio), + InkWell( + key: const Key('createRootDerivation'), + onTap: () async { + await Navigator.push( + context, + MaterialPageRoute( + builder: (context) { + return const CustomDerivation(); + }, + ), + ); + }, + child: SizedBox( + height: 50, + child: Row(children: const <Widget>[ + SizedBox(width: 35), + Icon( + Icons.manage_accounts, + size: 33, + ), + SizedBox(width: 25), + Text( + 'Créer une autre dérivation', + style: TextStyle(fontSize: 20, color: Colors.black), + ), + ]), + ), + ), + SizedBox(height: 10 * ratio), InkWell( key: const Key('deleteChest'), onTap: () async { @@ -144,7 +174,7 @@ class ChestOptions extends StatelessWidget { child: SizedBox( height: 50, child: Row(children: <Widget>[ - const SizedBox(width: 30), + const SizedBox(width: 28), Image.asset( 'assets/walletOptions/trash.png', height: 45, diff --git a/lib/screens/myWallets/custom_derivations.dart b/lib/screens/myWallets/custom_derivations.dart new file mode 100644 index 0000000000000000000000000000000000000000..2d2f46e533335b33a03a211b5bffdb8c703be4e1 --- /dev/null +++ b/lib/screens/myWallets/custom_derivations.dart @@ -0,0 +1,161 @@ +import 'package:flutter/services.dart'; +import 'package:gecko/globals.dart'; +import 'package:flutter/material.dart'; +import 'package:gecko/models/wallet_data.dart'; +import 'package:gecko/providers/my_wallets.dart'; +import 'package:gecko/screens/myWallets/unlocking_wallet.dart'; +import 'package:provider/provider.dart'; + +class CustomDerivation extends StatefulWidget { + const CustomDerivation({Key? key}) : super(key: key); + + @override + State<CustomDerivation> createState() => _CustomDerivationState(); +} + +class _CustomDerivationState extends State<CustomDerivation> { + String? dropdownValue; + + @override + void initState() { + dropdownValue = 'root'; + super.initState(); + } + + @override + Widget build(BuildContext context) { + SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]); + MyWalletsProvider _myWalletProvider = + Provider.of<MyWalletsProvider>(context, listen: false); + + final derivationList = <String>[ + 'root', + for (var i = 0; i < 50; i += 1) i.toString() + ]; + + final listWallets = _myWalletProvider.readAllWallets(); + + for (WalletData _wallet in listWallets) { + derivationList.remove(_wallet.derivation.toString()); + if (_wallet.derivation == -1) { + derivationList.remove('root'); + } + } + + if (!derivationList.contains(dropdownValue)) { + dropdownValue = derivationList.first; + } + + return Scaffold( + backgroundColor: backgroundColor, + appBar: AppBar( + toolbarHeight: 60 * ratio, + title: const SizedBox( + height: 22, + child: Text('Créer une dérivation personnalisé'), + )), + body: Center( + child: SafeArea( + child: Column(children: <Widget>[ + const Spacer(), + const Text( + 'Choisissez une dérivation:', + ), + const SizedBox(height: 20), + SizedBox( + width: 100, + child: DropdownButton<String>( + value: dropdownValue, + menuMaxHeight: 300, + icon: const Icon(Icons.arrow_downward), + elevation: 16, + style: TextStyle(color: orangeC), + underline: Container( + height: 2, + color: orangeC, + ), + onChanged: (String? newValue) { + setState(() { + dropdownValue = newValue!; + }); + }, + items: derivationList + .map<DropdownMenuItem<String>>((String value) { + return DropdownMenuItem<String>( + value: value, + child: SizedBox( + width: 75, + child: Row(children: [ + const Spacer(), + Text( + value, + style: const TextStyle( + fontSize: 20, color: Colors.black), + ), + const Spacer(), + ]), + )); + }).toList(), + ), + ), + const Spacer(flex: 1), + SizedBox( + width: 410, + height: 70, + child: ElevatedButton( + style: ElevatedButton.styleFrom( + elevation: 4, + primary: orangeC, // background + onPrimary: Colors.white, // foreground + ), + onPressed: () async { + WalletData? defaultWallet = + _myWalletProvider.getDefaultWallet(); + String? _pin; + if (_myWalletProvider.pinCode == '') { + _pin = await Navigator.push( + context, + MaterialPageRoute( + builder: (homeContext) { + return UnlockingWallet(wallet: defaultWallet); + }, + ), + ); + } + + if (_pin != null || _myWalletProvider.pinCode != '') { + String _newDerivationName = + 'Portefeuille ${_myWalletProvider.listWallets.last.number! + 2}'; + if (dropdownValue == 'root') { + await _myWalletProvider.generateRootWallet( + context, 'Portefeuille racine'); + } else { + await _myWalletProvider.generateNewDerivation( + context, + _newDerivationName, + int.parse(dropdownValue!), + ); + } + Navigator.pop(context); + Navigator.pop(context); + // Navigator.push( + // context, + // MaterialPageRoute(builder: (context) { + // return const WalletsHome(); + // }), + // ); + } + }, + child: const Text( + 'Valider', + style: TextStyle(fontSize: 24, fontWeight: FontWeight.w600), + ), + ), + ), + const Spacer(), + ]), + ), + ), + ); + } +} diff --git a/lib/screens/myWallets/unlocking_wallet.dart b/lib/screens/myWallets/unlocking_wallet.dart index 0237aa3d4a9422065bb5b561530c8af3a95adc94..249ce71a13e8505d019a28ba81ffbba2e41e5e66 100644 --- a/lib/screens/myWallets/unlocking_wallet.dart +++ b/lib/screens/myWallets/unlocking_wallet.dart @@ -37,10 +37,6 @@ class UnlockingWallet extends StatelessWidget { currentChestNumber = configBox.get('currentChest'); currentChest = chestBox.get(currentChestNumber)!; - if (configBox.get('isCacheChecked') == null) { - configBox.put('isCacheChecked', false); - } - int _pinLenght = _walletOptions.getPinLenght(wallet!.number); errorController = StreamController<ErrorAnimationType>(); @@ -222,7 +218,6 @@ class UnlockingWallet extends StatelessWidget { ], onCompleted: (_pin) async { _myWalletProvider.pinCode = _pin.toUpperCase(); - final isValid = await _sub.checkPassword( defaultWallet!.address!, _pin.toUpperCase()); diff --git a/pubspec.yaml b/pubspec.yaml index 05e18bdc25b85f43fc6268423d16ddf8ed584c9f..563fb9ca56dd601ee6321cefc75c0d0854a265c4 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -5,7 +5,7 @@ description: Pay with G1. # pub.dev using `pub publish`. This is preferred for private packages. publish_to: 'none' # Remove this line if you wish to publish to pub.dev -version: 0.0.7+9 +version: 0.0.7+10 environment: sdk: '>=2.12.0 <3.0.0'