diff --git a/assets/translations/en.json b/assets/translations/en.json
index a9230c2216141e8624419c696eb6e0e665c1eb4a..9117028c48318bcf0b26cd761edaaa325d4239ba 100644
--- a/assets/translations/en.json
+++ b/assets/translations/en.json
@@ -285,5 +285,31 @@
   "contact_about_me": "Profile",
   "contact_address": "Physical address",
   "contact_socials": "Social Media and Websites",
-  "info": "Info"
+  "info": "Info",
+  "other_auth_methods": "Other authentication methods",
+  "wallet_key_prefix": "keychain",
+  "keyfile_auth": "Authentication using keyfile",
+  "select_auth_file": "Select the authentication file",
+  "cesium_compatible_exports": "Keyfile Exports (compatible with Cesium)",
+  "export_pubsec_format": "Export as PubSec File",
+  "export_pubsec_format_description": "Generates a file that allows you to authenticate without entering your credentials. Keep it secure, as it holds sensitive data!",
+  "export_wif_format": "Export as WIF File",
+  "export_wif_format_description": "Creates a WIF file with your wallet for easy access. Keep it also secure, as it holds sensitive data!",
+  "export_ewif_format": "Export as Encrypted WIF (EWIF) File",
+  "export_ewif_format_description": "Generates an encrypted WIF file, requiring a password to access your wallet. This is more secure but still handle with care!",
+  "continue": "CONTINUE",
+  "ewif_intro": "Introduce a password to encrypt your WIF file",
+  "enter_a_password": "Enter a password",
+  "enter_password": "Enter Password",
+  "confirm_password": "Confirm password",
+  "password": "Password",
+  "password_hint": "Enter the password for this file",
+  "password_empty_error": "Password cannot be empty",
+  "ewif_parse_error": "Failed to parse EWIF file.",
+  "qr_scan_error_empty": "No valid QR data found.",
+  "auth_file_error": "Failed to parse the authentication file.",
+  "auth_file_pubkey_mismatch": "The public key from the file does not match.",
+  "scan_qr_auth": "Scan a QR code",
+  "qr_scanner_no_qr_detected": "No QR code detected. Please try again.",
+  "qr_scanner_error": "An error occurred while scanning the QR code."
 }
diff --git a/assets/translations/es.json b/assets/translations/es.json
index e93954c18333e567d8f1f50b7a66f55b2ebf4e6b..c0d32911aedef9d629650df8f123139e7ffea7a1 100644
--- a/assets/translations/es.json
+++ b/assets/translations/es.json
@@ -291,5 +291,31 @@
   "contact_address": "Dirección",
   "contact_socials": "Redes sociales y web",
   "no_transactions_simple": "Este monedero no tiene saldo",
-  "info": "Info"
+  "info": "Info",
+  "other_auth_methods": "Otras formas de autenticación",
+  "wallet_key_prefix": "llavero",
+  "keyfile_auth": "Autenticación con archivo de llave",
+  "select_auth_file": "Selecciona un archivo de autenticación",
+  "cesium_compatible_exports": "Exportaciones de Llaves (compatible con Cesium)",
+  "export_pubsec_format": "Exportar como Archivo PubSec",
+  "export_pubsec_format_description": "Genera un archivo que permite autenticarte sin ingresar tus credenciales. Mantenlo seguro, ya que contiene datos sensibles.",
+  "export_wif_format": "Exportar como Archivo WIF",
+  "export_wif_format_description": "Crea un archivo WIF con tu monedero para un acceso fácil. Mantenlo también seguro, ya que contiene datos sensibles.",
+  "export_ewif_format": "Exportar como Archivo WIF Encriptado (EWIF)",
+  "export_ewif_format_description": "Genera un archivo WIF encriptado que requiere una contraseña para acceder a tu monedero. Es más seguro, pero igual manéjalo con cuidado.",
+  "continue": "CONTINUA",
+  "ewif_intro": "Introduce una contraseña para encriptar tu archivo WIF",
+  "enter_a_password": "Introduce una contraseña",
+  "enter_password": "Introduce contraseña",
+  "confirm_password": "Confirmar contraseña",
+  "password": "Contraseña",
+  "password_hint": "Introduce la contraseña de este fichero",
+  "password_empty_error": "La contraseña no puede estar vacía",
+  "ewif_parse_error": "Error al desencriptar el archivo EWIF",
+  "qr_scan_error_empty": "No se encontraron datos válidos en el QR.",
+  "auth_file_error": "Error al procesar el archivo de autenticación.",
+  "auth_file_pubkey_mismatch": "La clave pública del archivo no coincide.",
+  "scan_qr_auth": "Escanear QR",
+  "qr_scanner_no_qr_detected": "No se detectó ningún código QR. Inténtalo de nuevo.",
+  "qr_scanner_error": "Ocurrió un error al escanear el código QR."
 }
diff --git a/lib/g1/g1_export_utils.dart b/lib/g1/g1_export_utils.dart
new file mode 100644
index 0000000000000000000000000000000000000000..21045e21ff0c542e634f91746afb7f0403176b35
--- /dev/null
+++ b/lib/g1/g1_export_utils.dart
@@ -0,0 +1,260 @@
+import 'dart:typed_data';
+
+import 'package:crypto/crypto.dart';
+import 'package:durt/durt.dart';
+import 'package:easy_localization/easy_localization.dart';
+import 'package:fast_base58/fast_base58.dart';
+import 'package:flutter/material.dart';
+import 'package:pointycastle/pointycastle.dart';
+
+import 'g1_helper.dart';
+
+String generateWif(CesiumWallet wallet) {
+  final Uint8List seed = wallet.seed;
+  if (seed.length != 32) {
+    throw ArgumentError('Private key must be 32 bytes long');
+  }
+  final List<int> wifData = <int>[0x01];
+  wifData.addAll(seed);
+  final List<int> doubleHash =
+      sha256.convert(sha256.convert(Uint8List.fromList(wifData)).bytes).bytes;
+  final List<int> checksum = doubleHash.sublist(0, 2);
+  wifData.addAll(checksum);
+  return Base58Encode(Uint8List.fromList(wifData));
+}
+
+String generateEwif(CesiumWallet wallet, String password) {
+  final Uint8List seed = wallet.seed;
+  final Uint8List signPk = Uint8List.fromList(Base58Decode(wallet.pubkey));
+  final List<int> ewifData = <int>[0x02];
+  final List<int> salt =
+      sha256.convert(sha256.convert(signPk).bytes).bytes.sublist(0, 4);
+  ewifData.addAll(salt);
+  final ScryptParameters scryptParams =
+      ScryptParameters(16384, 8, 8, 64, Uint8List.fromList(salt));
+  final KeyDerivator scrypt = KeyDerivator('scrypt')..init(scryptParams);
+  final Uint8List scryptSeed =
+      scrypt.process(Uint8List.fromList(password.codeUnits));
+  final Uint8List derivedhalf1 = scryptSeed.sublist(0, 32);
+  final Uint8List derivedhalf2 = scryptSeed.sublist(32, 64);
+  final Uint8List xorHalf1 = Uint8List(16);
+  final Uint8List xorHalf2 = Uint8List(16);
+  for (int i = 0; i < 16; i++) {
+    xorHalf1[i] = seed[i] ^ derivedhalf1[i];
+    xorHalf2[i] = seed[i + 16] ^ derivedhalf1[i + 16];
+  }
+
+  final Uint8List encryptedHalf1 = encryptAes(xorHalf1, derivedhalf2);
+  final Uint8List encryptedHalf2 = encryptAes(xorHalf2, derivedhalf2);
+
+  ewifData.addAll(encryptedHalf1);
+  ewifData.addAll(encryptedHalf2);
+
+  final List<int> checksum = sha256
+      .convert(sha256.convert(Uint8List.fromList(ewifData)).bytes)
+      .bytes
+      .sublist(0, 2);
+  ewifData.addAll(checksum);
+
+  return Base58Encode(Uint8List.fromList(ewifData));
+}
+
+final String keyFileNamePrefix = tr('wallet_key_prefix');
+
+Map<String, String> generatePubSecFile(String pubKey, String secKey) {
+  final String fileName = 'g1-$keyFileNamePrefix-$pubKey-PubSec.dunikey';
+  final String content = '''
+Type: PubSec
+Version: 1
+pub: $pubKey
+sec: $secKey
+''';
+  return <String, String>{fileName: content};
+}
+
+Map<String, String> generateWifFile(String pubKey, String wifData) {
+  final String fileName = 'g1-$keyFileNamePrefix-$pubKey-WIF.dunikey';
+  final String content = '''
+Type: WIF
+Version: 1
+Data: $wifData
+''';
+  return <String, String>{fileName: content};
+}
+
+Map<String, String> generateEwifFile(String pubKey, String ewifData) {
+  final String fileName = 'g1-$keyFileNamePrefix-$pubKey-EWIF.dunikey';
+  final String content = '''
+Type: EWIF
+Version: 1
+Data: $ewifData
+''';
+  return <String, String>{fileName: content};
+}
+
+String getPrivKey(CesiumWallet wallet) {
+  final Uint8List privKeyComplete =
+      Uint8List.fromList(wallet.seed + wallet.rootKey.publicKey);
+  return Base58Encode(privKeyComplete);
+}
+
+Future<CesiumWallet> parseKeyFile(String fileContent,
+    [BuildContext? context, String? password]) async {
+  final RegExp typeRegExp = RegExp(r'^Type: (\w+)', multiLine: true);
+  final RegExp pubRegExp = RegExp(r'pub: ([a-zA-Z0-9]+)', multiLine: true);
+  final RegExp secRegExp = RegExp(r'sec: ([a-zA-Z0-9]+)', multiLine: true);
+  final RegExp dataRegExp = RegExp(r'Data: ([a-zA-Z0-9]+)', multiLine: true);
+
+  final Match? typeMatch = typeRegExp.firstMatch(fileContent);
+  if (typeMatch == null) {
+    throw const FormatException('We cannot detect the type of the file.');
+  }
+
+  final String fileType = typeMatch.group(1)!;
+  switch (fileType) {
+    case 'PubSec':
+      return _parsePubSec(fileContent, pubRegExp, secRegExp);
+    case 'WIF':
+      return _parseWif(fileContent, dataRegExp);
+    case 'EWIF':
+      return password != null && password.isNotEmpty
+          ? _parseEwif(fileContent, password, dataRegExp)
+          : _promptPasswordForEwif(context!, fileContent, dataRegExp);
+    default:
+      throw FormatException('Type $fileType not supported.');
+  }
+}
+
+Future<CesiumWallet> _promptPasswordForEwif(
+    BuildContext context, String fileContent, RegExp dataRegExp) async {
+  final TextEditingController passwordController = TextEditingController();
+  final CesiumWallet? wallet = await showDialog<CesiumWallet>(
+    context: context,
+    builder: (BuildContext context) {
+      return AlertDialog(
+        title: Text(tr('enter_password')),
+        content: TextField(
+          controller: passwordController,
+          obscureText: true,
+          decoration: InputDecoration(
+            labelText: tr('password'),
+            hintText: tr('password_hint'),
+          ),
+        ),
+        actions: <Widget>[
+          TextButton(
+            onPressed: () => Navigator.of(context).pop(),
+            child: Text(tr('cancel')),
+          ),
+          TextButton(
+            onPressed: () {
+              final String password = passwordController.text.trim();
+              if (password.isEmpty) {
+                ScaffoldMessenger.of(context).showSnackBar(
+                  SnackBar(
+                    content: Text(tr('password_empty_error')),
+                    backgroundColor: Colors.red,
+                  ),
+                );
+                return;
+              }
+              try {
+                final CesiumWallet wallet =
+                    _parseEwif(fileContent, password, dataRegExp);
+                Navigator.of(context).pop(wallet);
+              } catch (e) {
+                ScaffoldMessenger.of(context).showSnackBar(
+                  SnackBar(
+                    content: Text(tr('ewif_parse_error')),
+                    backgroundColor: Colors.red,
+                  ),
+                );
+              }
+            },
+            child: Text(tr('ok')),
+          ),
+        ],
+      );
+    },
+  );
+
+  if (wallet == null) {
+    throw const FormatException('EWIF file parsing was cancelled.');
+  }
+
+  return wallet;
+}
+
+CesiumWallet _parsePubSec(
+    String fileContent, RegExp pubRegExp, RegExp secRegExp) {
+  final Match? pubMatch = pubRegExp.firstMatch(fileContent);
+  final Match? secMatch = secRegExp.firstMatch(fileContent);
+
+  if (pubMatch == null || secMatch == null) {
+    throw const FormatException('Missing data in PubSec file.');
+  }
+
+  final Uint8List privKeyComplete =
+      Uint8List.fromList(Base58Decode(secMatch.group(1)!));
+
+  final Uint8List privKey = privKeyComplete.sublist(0, 32);
+
+  return CesiumWallet.fromSeed(privKey);
+}
+
+CesiumWallet _parseWif(String fileContent, RegExp dataRegExp) {
+  final Match? dataMatch = dataRegExp.firstMatch(fileContent);
+  if (dataMatch == null) {
+    throw const FormatException('Missing data in WIF file.');
+  }
+
+  final Uint8List wifBytes =
+      Uint8List.fromList(Base58Decode(dataMatch.group(1)!));
+  final Uint8List privKey =
+      wifBytes.sublist(1, 33); // Exclude prefix and checksum
+
+  if (privKey.length != 32) {
+    throw FormatException(
+        'Private key length is not 32 bytes, found: ${privKey.length}');
+  }
+
+  return CesiumWallet.fromSeed(privKey);
+}
+
+CesiumWallet _parseEwif(
+    String fileContent, String password, RegExp dataRegExp) {
+  final Match? dataMatch = dataRegExp.firstMatch(fileContent);
+  if (dataMatch == null) {
+    throw const FormatException('Wrong data in EWIF file.');
+  }
+
+  final Uint8List ewifBytes =
+      Uint8List.fromList(Base58Decode(dataMatch.group(1)!));
+  final Uint8List salt = ewifBytes.sublist(1, 5);
+  final Uint8List encryptedHalf1 = ewifBytes.sublist(5, 21);
+  final Uint8List encryptedHalf2 = ewifBytes.sublist(21, 37);
+
+  final ScryptParameters scryptParams = ScryptParameters(16384, 8, 8, 64, salt);
+  final KeyDerivator scrypt = KeyDerivator('scrypt')..init(scryptParams);
+  final Uint8List scryptSeed =
+      scrypt.process(Uint8List.fromList(password.codeUnits));
+
+  final Uint8List derivedhalf1 = scryptSeed.sublist(0, 32);
+  final Uint8List derivedhalf2 = scryptSeed.sublist(32, 64);
+
+  final Uint8List decryptedHalf1 = decryptAes(encryptedHalf1, derivedhalf2);
+  final Uint8List decryptedHalf2 = decryptAes(encryptedHalf2, derivedhalf2);
+
+  final Uint8List privKey = Uint8List(32);
+  for (int i = 0; i < 16; i++) {
+    privKey[i] = decryptedHalf1[i] ^ derivedhalf1[i];
+    privKey[i + 16] = decryptedHalf2[i] ^ derivedhalf1[i + 16];
+  }
+
+  if (privKey.length != 32) {
+    throw FormatException(
+        'Private key length is not 32 bytes, found: ${privKey.length}');
+  }
+
+  return CesiumWallet.fromSeed(privKey);
+}
diff --git a/lib/g1/g1_helper.dart b/lib/g1/g1_helper.dart
index 815695b551d85878b573dca1b7bd9c27242299de..44029e437bc669a0e1bafeda175a99554ea319ca 100644
--- a/lib/g1/g1_helper.dart
+++ b/lib/g1/g1_helper.dart
@@ -2,6 +2,7 @@ import 'dart:convert';
 import 'dart:math';
 import 'dart:typed_data';
 
+import 'package:crypto/crypto.dart' as crypto;
 import 'package:crypto/crypto.dart';
 import 'package:durt/durt.dart';
 import 'package:encrypt/encrypt.dart' as encrypt;
@@ -158,8 +159,8 @@ String pkChecksum(String pubkey) {
   }
 
   // Double SHA256 hash
-  final Digest firstHash = sha256.convert(signpkInt8);
-  final Digest secondHash = sha256.convert(firstHash.bytes);
+  final crypto.Digest firstHash = sha256.convert(signpkInt8);
+  final crypto.Digest secondHash = sha256.convert(firstHash.bytes);
 
   // Base58 encode and take the first 3 characters
   final String checksum = Base58Encode(secondHash.bytes).substring(0, 3);
@@ -395,3 +396,26 @@ List<Transaction> lastTx(List<Transaction> origTxs) {
           areDatesClose(DateTime.now(), tx.time, paymentTimeRange))
       .toList();
 }
+
+Uint8List encryptAes(Uint8List data, Uint8List key) {
+  final encrypt.Encrypter encrypter = encrypt.Encrypter(encrypt.AES(
+    encrypt.Key(key),
+    mode: encrypt.AESMode.ecb,
+    padding: null,
+  ));
+  final encrypt.Encrypted encrypted =
+      encrypter.encryptBytes(data, iv: encrypt.IV(Uint8List(16)));
+  return Uint8List.fromList(encrypted.bytes);
+}
+
+Uint8List decryptAes(Uint8List encryptedData, Uint8List key) {
+  final encrypt.Encrypter encrypter = encrypt.Encrypter(encrypt.AES(
+    encrypt.Key(key),
+    mode: encrypt.AESMode.ecb,
+    padding: null,
+  ));
+  final List<int> decrypted = encrypter.decryptBytes(
+      encrypt.Encrypted(encryptedData),
+      iv: encrypt.IV(Uint8List(16)));
+  return Uint8List.fromList(decrypted);
+}
diff --git a/lib/ui/screens/fifth_screen.dart b/lib/ui/screens/fifth_screen.dart
index cc76043b743dece9dd15f1ab76bba3e56b5dbf18..d8283ca25bbd0d916fed3abae764a31c86608a43 100644
--- a/lib/ui/screens/fifth_screen.dart
+++ b/lib/ui/screens/fifth_screen.dart
@@ -56,7 +56,8 @@ class _FifthScreenState extends State<FifthScreen> {
     );
   }
 
-  Future<void> _openWalletSelector(BuildContext context) async {
+  Future<void> _openWalletSelector(
+      BuildContext context, bool expertMode) async {
     _showMultiWalletSelectorDialog(context,
         (List<CesiumCard> selectedCards, bool exportContacts) {
       setState(() {
@@ -64,7 +65,8 @@ class _FifthScreenState extends State<FifthScreen> {
         _selectedWallets = selectedCards;
         _exportContacts = exportContacts;
       });
-      _showSelectExportMethodDialog();
+      _showSelectExportMethodDialog(
+          onlyOneWalletSelected: selectedCards.length == 1 && expertMode);
     });
   }
 
@@ -229,7 +231,7 @@ class _FifthScreenState extends State<FifthScreen> {
                           title: 'export_key$pluralSuffix',
                           icon: Icons.download,
                           onTap: () async {
-                            _openWalletSelector(context);
+                            _openWalletSelector(context, state.expertMode);
                           }),
                       GridItem(
                           title: 'import_key$pluralSuffix',
@@ -308,10 +310,12 @@ class _FifthScreenState extends State<FifthScreen> {
     });
   }
 
-  Future<void> _showSelectExportMethodDialog() async {
+  Future<void> _showSelectExportMethodDialog(
+      {required bool onlyOneWalletSelected}) async {
     final ExportType? method = await showDialog<ExportType>(
       context: context,
-      builder: (BuildContext context) => const SelectExportMethodDialog(),
+      builder: (BuildContext context) => SelectExportMethodDialog(
+          onlyOneWalletSelected: onlyOneWalletSelected),
     );
     if (method != null) {
       if (!mounted) {
diff --git a/lib/ui/screens/select_export_method_dialog.dart b/lib/ui/screens/select_export_method_dialog.dart
index 2af5e50f407299b36d34711272f3abd04e81af0e..f410ef48eb66a6d9e095cdad63c4b2ce2f8ae456 100644
--- a/lib/ui/screens/select_export_method_dialog.dart
+++ b/lib/ui/screens/select_export_method_dialog.dart
@@ -4,26 +4,123 @@ import 'package:flutter/material.dart';
 import '../widgets/fifth_screen/export_dialog.dart';
 
 class SelectExportMethodDialog extends StatelessWidget {
-  const SelectExportMethodDialog({super.key});
+  const SelectExportMethodDialog(
+      {super.key, this.onlyOneWalletSelected = false});
+
+  final bool onlyOneWalletSelected;
 
   @override
   Widget build(BuildContext context) {
-    return AlertDialog(
-      title: Text(tr('select_export_method')),
-      // content: Text(tr('select_export_method_desc')),
-      actions: <Widget>[
-        TextButton.icon(
-            icon: const Icon(Icons.file_present),
-            label: Text(tr('file_export')),
-            onPressed: () => Navigator.of(context).pop(ExportType.file)),
-        TextButton.icon(
-            icon: const Icon(Icons.content_paste),
-            label: Text(tr('clipboard_export')),
-            onPressed: () => Navigator.of(context).pop(ExportType.clipboard)),
+    return Dialog(
+      shape: RoundedRectangleBorder(
+        borderRadius: BorderRadius.circular(16.0),
+      ),
+      child: SingleChildScrollView(
+        child: Padding(
+          padding: const EdgeInsets.all(16.0),
+          child: Column(
+            mainAxisSize: MainAxisSize.min,
+            children: <Widget>[
+              Padding(
+                padding: const EdgeInsets.only(bottom: 16.0),
+                child: Text(
+                  tr('select_export_method'),
+                  style: Theme.of(context).textTheme.titleLarge,
+                ),
+              ),
+              Column(
+                mainAxisAlignment: MainAxisAlignment.spaceEvenly,
+                children: <Widget>[
+                  TextButton.icon(
+                    icon: const Icon(Icons.file_present),
+                    label: Text(tr('file_export')),
+                    onPressed: () => Navigator.of(context).pop(ExportType.file),
+                  ),
+                  TextButton.icon(
+                    icon: const Icon(Icons.content_paste),
+                    label: Text(tr('clipboard_export')),
+                    onPressed: () =>
+                        Navigator.of(context).pop(ExportType.clipboard),
+                  ),
+                  TextButton.icon(
+                    icon: const Icon(Icons.share),
+                    label: Text(tr('share_export')),
+                    onPressed: () =>
+                        Navigator.of(context).pop(ExportType.share),
+                  ),
+                ],
+              ),
+              if (onlyOneWalletSelected) _buildKeyExportContent(context),
+            ],
+          ),
+        ),
+      ),
+    );
+  }
+
+  Widget _buildKeyExportContent(BuildContext context) {
+    return Padding(
+      padding: const EdgeInsets.only(top: 16.0),
+      child: Column(
+        mainAxisSize: MainAxisSize.min,
+        crossAxisAlignment: CrossAxisAlignment.start,
+        children: <Widget>[
+          const Divider(),
+          const SizedBox(height: 8),
+          Text(
+            tr('cesium_compatible_exports'),
+            style: Theme.of(context).textTheme.titleMedium,
+          ),
+          const SizedBox(height: 8),
+          _buildExportOption(
+            context,
+            ExportType.pubsec,
+            tr('export_pubsec_format'),
+            tr('export_pubsec_format_description'),
+          ),
+          _buildExportOption(
+            context,
+            ExportType.wif,
+            tr('export_wif_format'),
+            tr('export_wif_format_description'),
+          ),
+          _buildExportOption(
+            context,
+            ExportType.ewif,
+            tr('export_ewif_format'),
+            tr('export_ewif_format_description'),
+          ),
+        ],
+      ),
+    );
+  }
+
+  Widget _buildExportOption(
+    BuildContext context,
+    ExportType type,
+    String buttonText,
+    String descriptionText,
+  ) {
+    return Column(
+      crossAxisAlignment: CrossAxisAlignment.start,
+      children: <Widget>[
         TextButton.icon(
-            icon: const Icon(Icons.share),
-            label: Text(tr('share_export')),
-            onPressed: () => Navigator.of(context).pop(ExportType.share)),
+          icon: const Icon(Icons.article_outlined),
+          label: Text(buttonText),
+          onPressed: () {
+            Navigator.of(context).pop(type);
+          },
+        ),
+        Padding(
+          padding: const EdgeInsets.only(left: 16, right: 16, bottom: 8),
+          child: Text(
+            descriptionText,
+            style: Theme.of(context).textTheme.bodySmall?.copyWith(
+                  color: Colors.grey[700],
+                ),
+            textAlign: TextAlign.left,
+          ),
+        ),
       ],
     );
   }
diff --git a/lib/ui/widgets/cesium_auth_dialog.dart b/lib/ui/widgets/cesium_auth_dialog.dart
index 91ddab641afcbc74916d13f5613edc5d124b6682..1b0aadd1b736f223a7bfc50b0d70c98e2c5b9c19 100644
--- a/lib/ui/widgets/cesium_auth_dialog.dart
+++ b/lib/ui/widgets/cesium_auth_dialog.dart
@@ -2,17 +2,23 @@ import 'dart:math';
 
 import 'package:durt/durt.dart';
 import 'package:easy_localization/easy_localization.dart';
+import 'package:flutter/foundation.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 
+import '../../data/models/app_cubit.dart';
 import '../../data/models/bottom_nav_cubit.dart';
 import '../../data/models/cesium_card.dart';
 import '../../data/models/contact.dart';
 import '../../data/models/credit_card_themes.dart';
 import '../../g1/api.dart';
+import '../../g1/g1_export_utils.dart';
 import '../../g1/g1_helper.dart';
 import '../../shared_prefs_helper.dart';
+import '../logger.dart';
+import '../qr_manager.dart';
 import '../ui_helpers.dart';
+import 'fifth_screen/import_dialog.dart';
 import 'form_error_widget.dart';
 
 class CesiumAddDialog extends StatefulWidget {
@@ -40,120 +46,226 @@ class _CesiumAddDialogState extends State<CesiumAddDialog> {
       future: getProfile(widget.publicKey, onlyCPlusProfile: true),
       builder: (BuildContext context, AsyncSnapshot<Contact> snapshot) {
         if (snapshot.hasData) {
-          return showDialog(
-            context,
-            snapshot.data!,
-          );
+          return _buildCustomAlertDialog(context, snapshot.data!);
         }
-        return showDialog(context, Contact(pubKey: widget.publicKey));
+        return _buildCustomAlertDialog(
+            context, Contact(pubKey: widget.publicKey));
       },
     );
   }
 
-  AlertDialog showDialog(BuildContext context, Contact contact) {
+  AlertDialog _buildCustomAlertDialog(BuildContext context, Contact contact) {
     return AlertDialog(
-      title: Text(tr('cesium_auth_dialog_title', namedArgs: <String, String>{
-        'key': humanizeContact(widget.publicKey, contact)
-      })),
-      content: SingleChildScrollView(
-        child: ListBody(
-          children: <Widget>[
-            TextField(
-              controller: secretPhraseController,
-              obscureText: _obscureText1,
-              onChanged: (String? value) {
-                _feedbackNotifier.value = '';
+      title: Row(
+        mainAxisAlignment: MainAxisAlignment.spaceBetween,
+        children: <Widget>[
+          Text(tr('cesium_auth_dialog_title', namedArgs: <String, String>{
+            'key': humanizeContact(widget.publicKey, contact),
+          })),
+          if (context.read<AppCubit>().isExpertMode)
+            PopupMenuButton<String>(
+              tooltip: tr('other_auth_methods'),
+              onSelected: (String result) async {
+                if (result == 'import') {
+                  await _showFileImportDialog(context, contact);
+                } else if (result == 'scan') {
+                  await _showScanQrDialog(context, contact);
+                }
               },
-              decoration: InputDecoration(
-                labelText: tr('cesium_secret_phrase'),
-                suffixIcon: IconButton(
-                  icon: Icon(
-                    _obscureText1 ? Icons.visibility : Icons.visibility_off,
+              itemBuilder: (BuildContext context) => <PopupMenuEntry<String>>[
+                PopupMenuItem<String>(
+                  value: 'import',
+                  child: Row(
+                    children: <Widget>[
+                      const Icon(Icons.upload_file),
+                      const SizedBox(width: 8),
+                      Text(tr('keyfile_auth')),
+                    ],
                   ),
-                  onPressed: () {
-                    setState(() {
-                      _obscureText1 = !_obscureText1;
-                    });
-                  },
                 ),
-              ),
-            ),
-            TextField(
-              controller: passwordController,
-              obscureText: _obscureText2,
-              onChanged: (String? value) {
-                _feedbackNotifier.value = '';
-              },
-              decoration: InputDecoration(
-                labelText: tr('cesium_password'),
-                suffixIcon: IconButton(
-                  icon: Icon(
-                    _obscureText2 ? Icons.visibility : Icons.visibility_off,
+                PopupMenuItem<String>(
+                  value: 'scan',
+                  child: Row(
+                    children: <Widget>[
+                      const Icon(Icons.qr_code_scanner),
+                      const SizedBox(width: 8),
+                      Text(tr('scan_qr_auth')),
+                    ],
                   ),
-                  onPressed: () {
-                    setState(() {
-                      _obscureText2 = !_obscureText2;
-                    });
-                  },
                 ),
-              ),
+              ],
             ),
-            FormErrorWidget(feedbackNotifier: _feedbackNotifier),
-          ],
-        ),
+        ],
       ),
-      actions: <Widget>[
-        TextButton(
-          child: Text(tr('cancel')),
-          onPressed: () {
-            Navigator.of(context).pop(false);
-          },
-        ),
-        TextButton(
-          onPressed: _isProcessing
-              ? null
-              : () {
-                  _feedbackNotifier.value = '';
+      content: _buildDialogContent(context),
+      actions: _buildDialogActions(context, contact),
+    );
+  }
+
+  Widget _buildDialogContent(BuildContext context) {
+    return SingleChildScrollView(
+      child: ListBody(
+        children: <Widget>[
+          TextField(
+            controller: secretPhraseController,
+            obscureText: _obscureText1,
+            onChanged: (String? value) {
+              _feedbackNotifier.value = '';
+            },
+            decoration: InputDecoration(
+              labelText: tr('cesium_secret_phrase'),
+              suffixIcon: IconButton(
+                icon: Icon(
+                  _obscureText1 ? Icons.visibility : Icons.visibility_off,
+                ),
+                onPressed: () {
                   setState(() {
-                    _isProcessing = true;
+                    _obscureText1 = !_obscureText1;
                   });
-                  final String secret = secretPhraseController.text;
-                  final String password = passwordController.text;
-                  final CesiumWallet wallet = CesiumWallet(secret, password);
-
-                  // logger('wallet.pubkey: ${wallet.pubkey} vs ${widget.publicKey}');
+                },
+              ),
+            ),
+          ),
+          TextField(
+            controller: passwordController,
+            obscureText: _obscureText2,
+            onChanged: (String? value) {
+              _feedbackNotifier.value = '';
+            },
+            decoration: InputDecoration(
+              labelText: tr('cesium_password'),
+              suffixIcon: IconButton(
+                icon: Icon(
+                  _obscureText2 ? Icons.visibility : Icons.visibility_off,
+                ),
+                onPressed: () {
                   setState(() {
-                    _isProcessing = false;
+                    _obscureText2 = !_obscureText2;
                   });
-                  if (wallet.pubkey != extractPublicKey(widget.publicKey)) {
-                    _feedbackNotifier.value = tr('incorrect_passwords');
-                  } else {
-                    final CesiumCard card = CesiumCard(
-                        name: contact.name ?? '',
-                        pubKey: extractPublicKey(widget.publicKey),
-                        seed: '',
-                        theme: CreditCardThemes.themes[Random().nextInt(10)]);
-                    if (!SharedPreferencesHelper()
-                        .has(extractPublicKey(widget.publicKey))) {
-                      SharedPreferencesHelper().addCesiumCard(card);
-                      SharedPreferencesHelper().selectCurrentWallet(card);
-                    }
-                    SharedPreferencesHelper().addCesiumVolatileCard(wallet);
-                    if (context.read<BottomNavCubit>().currentIndex !=
-                        widget.returnTo) {
-                      context
-                          .read<BottomNavCubit>()
-                          .updateIndex(widget.returnTo);
-                    }
-                    _feedbackNotifier.value = '';
-                    Navigator.of(context).pop(true);
-                  }
                 },
-          child: _isProcessing
-              ? const CircularProgressIndicator()
-              : Text(tr('accept')),
-        ),
-      ],
+              ),
+            ),
+          ),
+          FormErrorWidget(feedbackNotifier: _feedbackNotifier),
+        ],
+      ),
     );
   }
+
+  List<Widget> _buildDialogActions(BuildContext context, Contact contact) {
+    return <Widget>[
+      TextButton(
+        child: Text(tr('cancel')),
+        onPressed: () {
+          Navigator.of(context).pop(false);
+        },
+      ),
+      TextButton(
+        onPressed: _isProcessing
+            ? null
+            : () async {
+                _feedbackNotifier.value = '';
+                setState(() {
+                  _isProcessing = true;
+                });
+                final String secret = secretPhraseController.text;
+                final String password = passwordController.text;
+                final CesiumWallet wallet = CesiumWallet(secret, password);
+
+                setState(() {
+                  _isProcessing = false;
+                });
+                if (wallet.pubkey != extractPublicKey(widget.publicKey)) {
+                  _feedbackNotifier.value = tr('incorrect_passwords');
+                } else {
+                  _onCorrectAuth(contact, wallet, context);
+                }
+              },
+        child: _isProcessing
+            ? const CircularProgressIndicator()
+            : Text(tr('accept')),
+      ),
+    ];
+  }
+
+  void _onCorrectAuth(
+      Contact contact, CesiumWallet wallet, BuildContext context) {
+    final CesiumCard card = CesiumCard(
+      name: contact.name ?? '',
+      pubKey: extractPublicKey(widget.publicKey),
+      seed: '',
+      theme: CreditCardThemes.themes[Random().nextInt(10)],
+    );
+    if (!SharedPreferencesHelper().has(extractPublicKey(widget.publicKey))) {
+      SharedPreferencesHelper().addCesiumCard(card);
+      SharedPreferencesHelper().selectCurrentWallet(card);
+    }
+    SharedPreferencesHelper().addCesiumVolatileCard(wallet);
+    if (context.read<BottomNavCubit>().currentIndex != widget.returnTo) {
+      context.read<BottomNavCubit>().updateIndex(widget.returnTo);
+    }
+    _feedbackNotifier.value = '';
+    Navigator.of(context).pop(true);
+  }
+
+  Future<void> _showFileImportDialog(BuildContext c, Contact contact) async {
+    if (!c.mounted) {
+      return;
+    }
+    String? fileContent;
+    if (kIsWeb) {
+      fileContent = await importWalletWeb(c, '.dunikey');
+    } else {
+      fileContent =
+          await importWallet(c, <String>['.dunikey'], 'select_auth_file');
+    }
+
+    if (fileContent != null && fileContent.isNotEmpty && mounted) {
+      try {
+        final CesiumWallet importedWallet =
+            await parseKeyFile(fileContent, context);
+
+        // loggerDev('Imported wallet: ${importedWallet.pubkey}');
+        // loggerDev('Wallet to auth: ${extractPublicKey(widget.publicKey)}');
+        if (importedWallet.pubkey == extractPublicKey(widget.publicKey)) {
+          if (!mounted) {
+            return;
+          }
+          _onCorrectAuth(contact, importedWallet, context);
+        } else {
+          _feedbackNotifier.value = tr('auth_file_pubkey_mismatch');
+        }
+      } catch (e) {
+        _feedbackNotifier.value = tr('auth_file_error');
+      }
+    }
+  }
+
+  Future<void> _showScanQrDialog(BuildContext context, Contact contact) async {
+    try {
+      final String? scannedKey = await QrManager.qrScan(context);
+
+      if (scannedKey != null && scannedKey.isNotEmpty) {
+        if (!context.mounted) {
+          return;
+        }
+        final CesiumWallet importedWallet =
+            await parseKeyFile(scannedKey, context);
+
+        if (importedWallet.pubkey == extractPublicKey(widget.publicKey)) {
+          if (!context.mounted) {
+            return;
+          }
+          _onCorrectAuth(contact, importedWallet, context);
+        } else {
+          _feedbackNotifier.value = tr('auth_file_pubkey_mismatch');
+        }
+      } else {
+        _feedbackNotifier.value = tr('qr_scan_error_empty');
+      }
+    } catch (e) {
+      logger('Error scanning QR: $e');
+      _feedbackNotifier.value = tr('auth_file_error');
+    }
+  }
 }
diff --git a/lib/ui/widgets/fifth_screen/export_dialog.dart b/lib/ui/widgets/fifth_screen/export_dialog.dart
index ac46e897b628fa74d708a2acc0e60ec58543489a..dddcc5b4599f41c89e8fc59ee09eeb3975d43586 100644
--- a/lib/ui/widgets/fifth_screen/export_dialog.dart
+++ b/lib/ui/widgets/fifth_screen/export_dialog.dart
@@ -3,8 +3,8 @@ import 'dart:convert';
 import 'dart:io';
 
 import 'package:clipboard/clipboard.dart';
+import 'package:durt/durt.dart';
 import 'package:easy_localization/easy_localization.dart';
-import 'package:file_saver/file_saver.dart';
 import 'package:flutter/foundation.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
@@ -17,19 +17,21 @@ import 'package:universal_html/html.dart' as html;
 import '../../../data/models/cesium_card.dart';
 import '../../../data/models/contact.dart';
 import '../../../data/models/contact_cubit.dart';
+import '../../../g1/g1_export_utils.dart';
 import '../../../g1/g1_helper.dart';
 import '../../logger.dart';
 import '../../ui_helpers.dart';
 import 'pattern_util.dart';
 
-enum ExportType { clipboard, file, share }
+enum ExportType { clipboard, file, share, pubsec, wif, ewif }
 
 class ExportDialog extends StatefulWidget {
-  const ExportDialog(
-      {super.key,
-      required this.type,
-      required this.wallets,
-      required this.exportContacts});
+  const ExportDialog({
+    super.key,
+    required this.type,
+    required this.wallets,
+    required this.exportContacts,
+  });
 
   final ExportType type;
   final List<CesiumCard> wallets;
@@ -48,65 +50,133 @@ class _ExportDialogState extends State<ExportDialog> {
 
   @override
   Widget build(BuildContext context) {
+    final bool requiresPattern = _requiresPattern(widget.type);
+
     return Scaffold(
       key: _exportKey,
       appBar: AppBar(
-        title: Text(tr('intro_some_pattern_to_export')),
+        title:
+            requiresPattern ? Text(tr('intro_some_pattern_to_export')) : null,
       ),
-      body: Column(
-        mainAxisAlignment: MainAxisAlignment.spaceEvenly,
-        children: <Widget>[
-          Flexible(
-            child: Text(
-              isConfirm ? tr('confirm_pattern') : tr('draw_pattern'),
-              style: const TextStyle(fontSize: 26),
-            ),
+      body: requiresPattern
+          ? _buildPatternLockScreen(context)
+          : widget.type == ExportType.ewif
+              ? _buildPasswordScreen(context)
+              : _executeExportDirectly(context),
+    );
+  }
+
+  Widget _buildPatternLockScreen(BuildContext context) {
+    return Column(
+      mainAxisAlignment: MainAxisAlignment.spaceEvenly,
+      children: <Widget>[
+        Flexible(
+          child: Text(
+            isConfirm ? tr('confirm_pattern') : tr('draw_pattern'),
+            style: const TextStyle(fontSize: 26),
           ),
-          Flexible(
-            child: PatternLock(
-              selectedColor: selectedPatternLock(),
-              notSelectedColor: notSelectedPatternLock(),
-              pointRadius: 12,
-              onInputComplete: (List<int> input) {
-                if (input.length < 3) {
+        ),
+        Flexible(
+          child: PatternLock(
+            selectedColor: selectedPatternLock(),
+            notSelectedColor: notSelectedPatternLock(),
+            pointRadius: 12,
+            onInputComplete: (List<int> input) {
+              if (input.length < 3) {
+                context.replaceSnackbar(
+                  content: Text(
+                    tr('at_least_3'),
+                    style: const TextStyle(color: Colors.red),
+                  ),
+                );
+                return;
+              }
+              if (isConfirm) {
+                if (listEquals<int>(input, pattern)) {
+                  Navigator.of(context).pop();
+                  _export(pattern!.join(), context, widget.type);
+                } else {
                   context.replaceSnackbar(
                     content: Text(
-                      tr('at_least_3'),
+                      tr('pattern_do_not_match'),
                       style: const TextStyle(color: Colors.red),
                     ),
                   );
-                  return;
-                }
-                if (isConfirm) {
-                  if (listEquals<int>(input, pattern)) {
-                    Navigator.of(context).pop();
-                    _export(pattern!.join(), context, widget.type);
-                  } else {
-                    context.replaceSnackbar(
-                      content: Text(
-                        tr('pattern_do_not_match'),
-                        style: const TextStyle(color: Colors.red),
-                      ),
-                    );
-                    setState(() {
-                      pattern = null;
-                      isConfirm = false;
-                    });
-                  }
-                } else {
                   setState(() {
-                    pattern = input;
-                    isConfirm = true;
+                    pattern = null;
+                    isConfirm = false;
                   });
                 }
-              },
+              } else {
+                setState(() {
+                  pattern = input;
+                  isConfirm = true;
+                });
+              }
+            },
+          ),
+        ),
+      ],
+    );
+  }
+
+  Widget _buildPasswordScreen(BuildContext context) {
+    final TextEditingController passwordController = TextEditingController();
+    final TextEditingController confirmPasswordController =
+        TextEditingController();
+
+    return Padding(
+      padding: const EdgeInsets.all(16.0),
+      child: Column(
+        mainAxisAlignment: MainAxisAlignment.center,
+        children: <Widget>[
+          Text(tr('ewif_intro')),
+          TextField(
+            controller: passwordController,
+            obscureText: true,
+            decoration: InputDecoration(
+              labelText: tr('enter_a_password'),
             ),
           ),
+          TextField(
+            controller: confirmPasswordController,
+            obscureText: true,
+            decoration: InputDecoration(
+              labelText: tr('confirm_password'),
+            ),
+          ),
+          const SizedBox(height: 16),
+          ElevatedButton(
+            onPressed: () {
+              final String password = passwordController.text;
+              final String confirmPassword = confirmPasswordController.text;
+
+              if (password != confirmPassword) {
+                context.replaceSnackbar(
+                  content: Text(
+                    tr('passwords_do_not_match'),
+                    style: const TextStyle(color: Colors.red),
+                  ),
+                );
+              } else {
+                Navigator.of(context).pop();
+                _export(password, context, widget.type);
+              }
+            },
+            child: Text(tr('export')),
+          ),
         ],
       ),
     );
   }
 
+  Widget _executeExportDirectly(BuildContext context) {
+    WidgetsBinding.instance.addPostFrameCallback((_) {
+      _export('', context, widget.type);
+    });
+    return const Center(child: CircularProgressIndicator());
+  }
+
   Future<void> _export(
       String password, BuildContext context, ExportType type) async {
     final ContactsCubit cubit = context.read<ContactsCubit>();
@@ -131,7 +201,6 @@ class _ExportDialogState extends State<ExportDialog> {
     loggerDev('Exporting: $jsonData and contacts: ${cubit.contacts.length}');
     final String fileJson = jsonEncode(jsonData);
     final List<int> bytes = utf8.encode(fileJson);
-
     switch (type) {
       case ExportType.clipboard:
         FlutterClipboard.copy(fileJson).then((dynamic value) {
@@ -148,13 +217,13 @@ class _ExportDialogState extends State<ExportDialog> {
       case ExportType.file:
         bool result = false;
         if (kIsWeb) {
-          webDownload(bytes);
+          _webFileDownload(bytes);
           result = true;
         } else {
           if (!context.mounted) {
             return;
           }
-          result = await saveFile(context, bytes);
+          result = await _saveFileNonWeb(context, bytes);
         }
         if (!context.mounted) {
           return;
@@ -174,9 +243,71 @@ class _ExportDialogState extends State<ExportDialog> {
         }
         shareExport(context, fileJson);
         break;
+      case ExportType.pubsec:
+        await _saveSpecialFile(context, generatePubSecFile, type);
+        if (!context.mounted) {
+          return;
+        }
+        Navigator.of(context).pop();
+        break;
+      case ExportType.wif:
+        await _saveSpecialFile(context, generateWifFile, type);
+        if (!context.mounted) {
+          return;
+        }
+        Navigator.of(context).pop();
+        break;
+      case ExportType.ewif:
+        await _saveSpecialFile(context, generateEwifFile, type,
+            password: password);
+        break;
     }
   }
 
+  bool _requiresPattern(ExportType type) {
+    return type == ExportType.clipboard ||
+        type == ExportType.file ||
+        type == ExportType.share;
+  }
+
+  Future<void> _saveSpecialFile(
+    BuildContext context,
+    Map<String, String> Function(String, String) generateFile,
+    ExportType type, {
+    String? password,
+  }) async {
+    final CesiumWallet wallet = _getFirstWallet();
+    final String pubKey = wallet.pubkey;
+    final String privKey =
+        password != null ? generateEwif(wallet, password) : getPrivKey(wallet);
+
+    final Map<String, String> fileResult = generateFile(pubKey, privKey);
+    final String fileName = fileResult.keys.first;
+    final String content = fileResult.values.first;
+    bool result = false;
+    if (kIsWeb) {
+      _webFileDownload(utf8.encode(content), fileName);
+      result = true;
+    } else {
+      result = await _saveFileNonWeb(context, utf8.encode(content), fileName);
+    }
+    if (context.mounted && result) {
+      context.replaceSnackbar(
+        content: Text(
+          tr('wallet_exported'),
+          style: const TextStyle(color: Colors.green),
+        ),
+      );
+    }
+  }
+
+  CesiumWallet _getFirstWallet() {
+    final CesiumCard card = widget.wallets.first;
+    final CesiumWallet wallet =
+        CesiumWallet.fromSeed(seedFromString(card.seed));
+    return wallet;
+  }
+
   Future<void> shareExport(BuildContext context, String fileJson) {
     if (kIsWeb) {
       final Uri uri = Uri.parse(html.window.location.href);
@@ -190,16 +321,17 @@ class _ExportDialogState extends State<ExportDialog> {
     }
   }
 
-  void webDownload(List<int> bytes) {
+  void _webFileDownload(List<int> bytes, [String? fileNameArg]) {
     final html.Blob blob = html.Blob(<dynamic>[bytes]);
     final String url = html.Url.createObjectUrlFromBlob(blob);
 
     final html.AnchorElement anchor = html.AnchorElement(href: url);
-    anchor.download = getWalletFileName();
+    anchor.download = fileNameArg ?? getWalletFileName();
     anchor.click();
   }
 
-  Future<bool> saveFile(BuildContext context, List<int> bytes) async {
+  Future<bool> _saveFileNonWeb(BuildContext context, List<int> bytes,
+      [String? fileNameArg]) async {
     try {
       final bool hasPermission = await requestStoragePermission(context);
       if (!hasPermission) {
@@ -208,34 +340,21 @@ class _ExportDialogState extends State<ExportDialog> {
 
       final Directory? directory = await getGinkgoDownloadDirectory();
       if (directory == null) {
-        logger('App files directory not found');
+        loggerDev('App files directory not found');
         return false;
       }
-      final String fileName = getWalletFileName();
+      final String fileName = fileNameArg ?? getWalletFileName();
       final File file = File(join(directory.path, fileName));
       await file.writeAsBytes(bytes);
-      logger('File saved at: ${file.path}');
+      loggerDev('File saved at: ${file.path}');
       return true;
     } catch (e, stacktrace) {
-      logger('Error saving wallet file $e');
+      loggerDev('Error saving wallet file $e');
       await Sentry.captureException(e, stackTrace: stacktrace);
       return false;
     }
   }
 
-  Future<void> saveFileApp(List<int> bytesList) async {
-    final Uint8List bytes = Uint8List.fromList(bytesList);
-
-    final String fileName = getWalletFileName();
-
-    await FileSaver.instance.saveFile(
-      name: fileName,
-      bytes: bytes,
-      // 'application/json',
-      mimeType: MimeType.json,
-    );
-  }
-
   String getWalletFileName() {
     final DateTime now = DateTime.now();
     final String formattedDate = todayS(now);
diff --git a/lib/ui/widgets/fifth_screen/import_dialog.dart b/lib/ui/widgets/fifth_screen/import_dialog.dart
index 0a201a7066cf6592ca1e392b35bea96b3ff936c0..2d3a7c67f76e8f03a4b1aee5c292411b60f7152e 100644
--- a/lib/ui/widgets/fifth_screen/import_dialog.dart
+++ b/lib/ui/widgets/fifth_screen/import_dialog.dart
@@ -33,14 +33,14 @@ class ImportDialog extends StatefulWidget {
 
 class _ImportDialogState extends State<ImportDialog> {
   final GlobalKey<ScaffoldState> _importKey =
-  GlobalKey<ScaffoldState>(debugLabel: 'importKey');
+      GlobalKey<ScaffoldState>(debugLabel: 'importKey');
   int _attempts = 0;
 
   @override
   Widget build(BuildContext c) {
     return FutureBuilder<String>(
         future: widget.wallet == null
-            ? (kIsWeb ? _importWalletWeb(c) : _importWallet(c))
+            ? (kIsWeb ? importWalletWeb(c) : importWallet(c))
             : Future<String>.value(widget.wallet),
         builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
           if (snapshot.hasData &&
@@ -48,7 +48,7 @@ class _ImportDialogState extends State<ImportDialog> {
               snapshot.data!.isNotEmpty) {
             final String keyEncString = snapshot.data!;
             final Map<String, dynamic> keyJson =
-            jsonDecode(keyEncString) as Map<String, dynamic>;
+                jsonDecode(keyEncString) as Map<String, dynamic>;
             final String keyEncrypted = keyJson['key'] as String;
             return Scaffold(
               key: _importKey,
@@ -86,17 +86,17 @@ class _ImportDialogState extends State<ImportDialog> {
                         try {
                           // try to decrypt
                           final Map<String, dynamic> keys =
-                          decryptJsonForImport(
-                              keyEncrypted, pattern.join());
+                              decryptJsonForImport(
+                                  keyEncrypted, pattern.join());
                           try {
                             final dynamic cesiumCards = keys['cesiumCards'];
                             final List<dynamic>? contacts =
-                            keys['contacts'] as List<dynamic>?;
+                                keys['contacts'] as List<dynamic>?;
                             importContacts(contacts, context);
                             if (cesiumCards != null) {
                               final List<dynamic> cesiumCardList =
-                              jsonDecode(cesiumCards as String)
-                              as List<dynamic>;
+                                  jsonDecode(cesiumCards as String)
+                                      as List<dynamic>;
                               // ignore: avoid_function_literals_in_foreach_calls
                               int imported = 0;
                               for (final dynamic cesiumCard in cesiumCardList) {
@@ -111,9 +111,9 @@ class _ImportDialogState extends State<ImportDialog> {
                                   imported == 0
                                       ? tr('no_wallets_imported')
                                       : tr('wallets_imported',
-                                      namedArgs: <String, String>{
-                                        'number': imported.toString()
-                                      }),
+                                          namedArgs: <String, String>{
+                                              'number': imported.toString()
+                                            }),
                                   style: TextStyle(
                                       color: imported == 0
                                           ? Colors.red
@@ -194,19 +194,19 @@ class _ImportDialogState extends State<ImportDialog> {
         if (existingContacts.isNotEmpty) {
           for (final dynamic contactJson in contacts) {
             final Contact contact =
-            Contact.fromJson(contactJson as Map<String, dynamic>);
+                Contact.fromJson(contactJson as Map<String, dynamic>);
             if (!contactsCubit.isContact(contact.pubKey)) {
               contactsCubit.addContact(contact);
             } else {
               final Contact storedContact =
-              contactsCubit.getContact(contact.pubKey)!;
+                  contactsCubit.getContact(contact.pubKey)!;
               contactsCubit.updateContact(storedContact.merge(contact));
             }
           }
         } else {
           for (final dynamic contactJson in contacts) {
             final Contact contact =
-            Contact.fromJson(contactJson as Map<String, dynamic>);
+                Contact.fromJson(contactJson as Map<String, dynamic>);
             contactsCubit.addContact(contact);
           }
         }
@@ -217,7 +217,7 @@ class _ImportDialogState extends State<ImportDialog> {
   bool importWalletToSharedPrefs(Map<String, dynamic> cesiumCard) {
     final dynamic pub = cesiumCard['pub'];
     final String pubKey =
-    pub != null ? pub as String : cesiumCard['pubKey'] as String;
+        pub != null ? pub as String : cesiumCard['pubKey'] as String;
     if (!SharedPreferencesHelper().has(pubKey)) {
       SharedPreferencesHelper().addCesiumCard(SharedPreferencesHelper()
           .buildCesiumCard(pubKey: pubKey, seed: cesiumCard['seed'] as String));
@@ -226,100 +226,10 @@ class _ImportDialogState extends State<ImportDialog> {
       return false;
     }
   }
-
-  Future<String> _importWallet(BuildContext context) async {
-    try {
-      final bool hasPermission = await requestStoragePermission(context);
-      if (!hasPermission) {
-        logger('No permission to access storage');
-        return '';
-      }
-
-      final Directory? directory = await getGinkgoDownloadDirectory();
-      if (directory == null) {
-        logger('App files directory not found');
-        return '';
-      }
-
-      logger('appDocDir: ${directory.path}');
-
-      if (!context.mounted) {
-        return '';
-      }
-
-      final String? filePath = await FilesystemPicker.openDialog(
-        title: tr('select_file_to_import'),
-        context: context,
-        rootDirectory: directory,
-        showGoUp: true,
-        fsType: FilesystemType.all,
-        allowedExtensions: <String>['.json'],
-        requestPermission: () async => requestStoragePermission(context),
-        fileTileSelectMode: FileTileSelectMode.wholeTile,
-      );
-
-      if (filePath == null || filePath.isEmpty) {
-        return '';
-      }
-
-      final File file = File(filePath);
-      final String jsonString = await file.readAsString();
-
-      // Log the content if not in release mode
-      if (!kReleaseMode) {
-        logger(jsonString);
-      }
-
-      return jsonString;
-    } catch (e, stacktrace) {
-      logger('Error importing wallet $e');
-      await Sentry.captureException(e, stackTrace: stacktrace);
-      // Handle the exception using Sentry or any other error reporting tool
-      // await Sentry.captureException(e, stackTrace: stacktrace);
-      return '';
-    }
-  }
-
-  Future<String> _importWalletWeb(BuildContext context) async {
-    final Completer<String> completer = Completer<String>();
-    final html.InputElement input = html.InputElement()
-      ..type = 'file';
-
-    input.multiple = false;
-    input.accept = '.json'; // limit file types
-    input.click();
-
-    input.onChange.listen((html.Event event) async {
-      if (input.files != null && input.files!.isEmpty) {
-        completer.complete('');
-        return;
-      }
-
-      final html.File file = input.files!.first;
-      final html.FileReader reader = html.FileReader();
-
-      // Read as text
-      reader.readAsText(file);
-      await reader.onLoadEnd.first;
-
-      try {
-        final String? jsonString = reader.result as String?;
-        if (jsonString != null && !kReleaseMode) {
-          logger(jsonString);
-        }
-        completer.complete(jsonString);
-      } catch (e, stacktrace) {
-        logger('Error importing wallet $e');
-        await Sentry.captureException(e, stackTrace: stacktrace);
-        completer.complete('');
-      }
-    });
-    return completer.future;
-  }
 }
 
-Future<void> showSelectImportMethodDialog(BuildContext context,
-    int returnTo) async {
+Future<void> showSelectImportMethodDialog(
+    BuildContext context, int returnTo) async {
   final String? method = await showDialog<String>(
     context: context,
     builder: (BuildContext context) => const SelectImportMethodDialog(),
@@ -358,8 +268,8 @@ Future<void> showSelectImportMethodDialog(BuildContext context,
   }
 }
 
-Future<bool?> showImportCesiumWalletDialog(BuildContext context, String wallet,
-    int returnTo) {
+Future<bool?> showImportCesiumWalletDialog(
+    BuildContext context, String wallet, int returnTo) {
   return showDialog<bool>(
     context: context,
     barrierDismissible: false,
@@ -406,3 +316,95 @@ class SelectImportMethodDialog extends StatelessWidget {
     );
   }
 }
+
+Future<String> importWalletWeb(BuildContext context,
+    [String allowedExtension = '.json']) async {
+  final Completer<String> completer = Completer<String>();
+  final html.InputElement input = html.InputElement()..type = 'file';
+
+  input.multiple = false;
+  input.accept = allowedExtension; // limit file types
+  input.click();
+
+  input.onChange.listen((html.Event event) async {
+    if (input.files != null && input.files!.isEmpty) {
+      completer.complete('');
+      return;
+    }
+
+    final html.File file = input.files!.first;
+    final html.FileReader reader = html.FileReader();
+
+    // Read as text
+    reader.readAsText(file);
+    await reader.onLoadEnd.first;
+
+    try {
+      final String? jsonString = reader.result as String?;
+      if (jsonString != null && !kReleaseMode) {
+        // logger(jsonString);
+      }
+      completer.complete(jsonString);
+    } catch (e, stacktrace) {
+      logger('Error importing wallet $e');
+      await Sentry.captureException(e, stackTrace: stacktrace);
+      completer.complete('');
+    }
+  });
+  return completer.future;
+}
+
+Future<String> importWallet(BuildContext context,
+    [List<String> allowedExtensions = const <String>['.json'],
+    String messageKey = 'select_file_to_import']) async {
+  try {
+    final bool hasPermission = await requestStoragePermission(context);
+    if (!hasPermission) {
+      logger('No permission to access storage');
+      return '';
+    }
+
+    final Directory? directory = await getGinkgoDownloadDirectory();
+    if (directory == null) {
+      logger('App files directory not found');
+      return '';
+    }
+
+    logger('appDocDir: ${directory.path}');
+
+    if (!context.mounted) {
+      return '';
+    }
+
+    final String? filePath = await FilesystemPicker.openDialog(
+      title: tr(messageKey),
+      context: context,
+      rootDirectory: directory,
+      showGoUp: true,
+      fsType: FilesystemType.all,
+      allowedExtensions: allowedExtensions,
+      requestPermission: () async => requestStoragePermission(context),
+      fileTileSelectMode: FileTileSelectMode.wholeTile,
+    );
+
+    if (filePath == null || filePath.isEmpty) {
+      return '';
+    }
+
+    final File file = File(filePath);
+    final String jsonString = await file.readAsString();
+
+    // Log the content if not in release mode
+    if (!kReleaseMode) {
+      // logger(jsonString);
+    }
+
+    return jsonString;
+  } catch (e, stacktrace) {
+    logger('Error importing wallet $e');
+    await Sentry.captureException(e, stackTrace: stacktrace);
+    // Handle the exception using Sentry or any other error reporting tool
+    // await Sentry.captureException(e, stackTrace: stacktrace);
+    return '';
+  }
+}
diff --git a/test/g1_test.dart b/test/g1_test.dart
index de9bc3407208026aeeaef8783d94c35d1733628e..50afa499cda875357021db99b66e8353e3c4ebeb 100644
--- a/test/g1_test.dart
+++ b/test/g1_test.dart
@@ -8,9 +8,22 @@ import 'package:ginkgo/data/models/contact.dart';
 import 'package:ginkgo/data/models/payment_state.dart';
 import 'package:ginkgo/data/models/transaction.dart';
 import 'package:ginkgo/data/models/transaction_type.dart';
+import 'package:ginkgo/g1/g1_export_utils.dart';
 import 'package:ginkgo/g1/g1_helper.dart';
 import 'package:ginkgo/ui/logger.dart';
 
+String _generateRandomPatternPassword(Random random) {
+  final int length = random.nextInt(8) + 2; // Password length between 2 and 9.
+  final Set<int> digits = <int>{1, 2, 3, 4, 5, 6, 7, 8, 9};
+  final List<int> passwordDigits = <int>[];
+  for (int i = 0; i < length; i++) {
+    final int selectedDigit = digits.elementAt(random.nextInt(digits.length));
+    passwordDigits.add(selectedDigit);
+    digits.remove(selectedDigit);
+  }
+  return passwordDigits.join();
+}
+
 void main() {
   const String testPubKey = '7wnDh2FPdwNW8Dd5JyoJTbspuu8b9QJKps2xAYenefsu';
   const String testPubKey1 = '7XtCpQSj8HRQxAD7rjZrMJ1knxBm6yx317R7sYzu3Hy6';
@@ -651,16 +664,96 @@ void main() {
     final String primaryKey = firstCard['pubKey'] as String;
     expect(primaryKey, equals(expectedPrimaryKey));
   });
-}
 
-String _generateRandomPatternPassword(Random random) {
-  final int length = random.nextInt(8) + 2; // Password length between 2 and 9.
-  final Set<int> digits = <int>{1, 2, 3, 4, 5, 6, 7, 8, 9};
-  final List<int> passwordDigits = <int>[];
-  for (int i = 0; i < length; i++) {
-    final int selectedDigit = digits.elementAt(random.nextInt(digits.length));
-    passwordDigits.add(selectedDigit);
-    digits.remove(selectedDigit);
-  }
-  return passwordDigits.join();
+  test('Create Cesium account with devtest/devtest and validate pubkey', () {
+    // Given
+    const String expectedPubKey =
+        '6SvSMyZSTUFtKo8BJEN959xRX4ze9K3WT7SBK9tqR5vh';
+    const String expectedSecKey =
+        '3WUvt7z9M2tNNfmQkYakJ12VGGGaLdVVjQu4wMYdo92CMuL3cnkf4Zr29dWyrGM2JKKYo6D1BkQsRVouV33s1zim';
+    const String expectedWifData =
+        '9dA4Ciza7hLv1ShRgb1XSqd95BDQtYEi31ZwH6gzCb1gJBE';
+    const String expectedEwifData =
+        '2RTjpjZMnFnKHhgUadgT7JUvGeQem5sC6DQQpeuo5dCL6V1fgqsg8';
+
+    const String password = 'devtest';
+
+    // When
+    final CesiumWallet wallet = CesiumWallet(password, password);
+
+    // Generate
+    final String secKey = getPrivKey(wallet);
+    final String wifData = generateWif(wallet);
+    final String ewifData = generateEwif(wallet, 'devtest');
+
+    // Generate files
+    final Map<String, String> pubSecFile =
+        generatePubSecFile(wallet.pubkey, secKey);
+    final Map<String, String> wifFile = generateWifFile(wallet.pubkey, wifData);
+    final Map<String, String> ewifFile =
+        generateEwifFile(wallet.pubkey, ewifData);
+
+    // Validate
+    expect(wallet.pubkey, equals(expectedPubKey));
+    expect(secKey, equals(expectedSecKey));
+    expect(wifData, equals(expectedWifData));
+    expect(ewifData, equals(expectedEwifData));
+    expect(pubSecFile.values.first, equals('''
+Type: PubSec
+Version: 1
+pub: $expectedPubKey
+sec: $expectedSecKey
+'''));
+
+    expect(wifFile.values.first, equals('''
+Type: WIF
+Version: 1
+Data: $expectedWifData
+'''));
+
+    expect(ewifFile.values.first, equals('''
+Type: EWIF
+Version: 1
+Data: $expectedEwifData
+'''));
+  });
+
+  test('Parse PubSec file and validate wallet', () async {
+    const String pubSecContent = '''
+Type: PubSec
+Version: 1
+pub: 6SvSMyZSTUFtKo8BJEN959xRX4ze9K3WT7SBK9tqR5vh
+sec: 3WUvt7z9M2tNNfmQkYakJ12VGGGaLdVVjQu4wMYdo92CMuL3cnkf4Zr29dWyrGM2JKKYo6D1BkQsRVouV33s1zim
+''';
+
+    final CesiumWallet wallet =
+        await parseKeyFile(pubSecContent, null, 'devtest');
+    expect(
+        wallet.pubkey, equals('6SvSMyZSTUFtKo8BJEN959xRX4ze9K3WT7SBK9tqR5vh'));
+  });
+
+  test('Parse WIF file and validate wallet', () async {
+    const String wifContent = '''
+Type: WIF
+Version: 1
+Data: 9dA4Ciza7hLv1ShRgb1XSqd95BDQtYEi31ZwH6gzCb1gJBE
+''';
+
+    final CesiumWallet wallet = await parseKeyFile(wifContent, null, 'devtest');
+    expect(
+        wallet.pubkey, equals('6SvSMyZSTUFtKo8BJEN959xRX4ze9K3WT7SBK9tqR5vh'));
+  });
+
+  test('Parse EWIF file and validate wallet', () async {
+    const String ewifContent = '''
+Type: EWIF
+Version: 1
+Data: 2RTjpjZMnFnKHhgUadgT7JUvGeQem5sC6DQQpeuo5dCL6V1fgqsg8
+''';
+
+    final CesiumWallet wallet =
+        await parseKeyFile(ewifContent, null, 'devtest');
+    expect(
+        wallet.pubkey, equals('6SvSMyZSTUFtKo8BJEN959xRX4ze9K3WT7SBK9tqR5vh'));
+  });
 }