Import mnemonic; can pay;

This commit is contained in:
poka 2022-02-19 23:51:12 +01:00
parent d2c89b1f14
commit fa31a2d4d8
6 changed files with 181 additions and 53 deletions

View File

@ -20,7 +20,7 @@ late Box<WalletData> walletBox;
late Box<ChestData> chestBox; late Box<ChestData> chestBox;
late Box configBox; late Box configBox;
late Box<G1WalletsList> g1WalletsBox; late Box<G1WalletsList> g1WalletsBox;
late Box keystoreBox; // late Box keystoreBox;
String cesiumPod = "https://g1.data.le-sou.org"; String cesiumPod = "https://g1.data.le-sou.org";
// String cesiumPod = "https://g1.data.e-is.pro"; // String cesiumPod = "https://g1.data.e-is.pro";

View File

@ -72,7 +72,7 @@ Future<void> main() async {
chestBox = await Hive.openBox<ChestData>("chestBox"); chestBox = await Hive.openBox<ChestData>("chestBox");
configBox = await Hive.openBox("configBox"); configBox = await Hive.openBox("configBox");
g1WalletsBox = await Hive.openBox<G1WalletsList>("g1WalletsBox"); g1WalletsBox = await Hive.openBox<G1WalletsList>("g1WalletsBox");
keystoreBox = await Hive.openBox("keystoreBox"); // keystoreBox = await Hive.openBox("keystoreBox");
g1WalletsBox.clear(); g1WalletsBox.clear();

View File

@ -7,6 +7,7 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'dart:async'; import 'dart:async';
import 'package:gecko/globals.dart'; import 'package:gecko/globals.dart';
import 'package:gecko/providers/substrate_sdk.dart';
import 'package:hive_flutter/hive_flutter.dart'; import 'package:hive_flutter/hive_flutter.dart';
import 'package:flutter/foundation.dart' show kIsWeb; import 'package:flutter/foundation.dart' show kIsWeb;
import 'package:path_provider/path_provider.dart' as pp; import 'package:path_provider/path_provider.dart' as pp;
@ -146,7 +147,7 @@ class HomeProvider with ChangeNotifier {
_message = _message =
"Aucun noeud Duniter disponible, veuillez réessayer ultérieurement"; "Aucun noeud Duniter disponible, veuillez réessayer ultérieurement";
} else { } else {
_message = "Vous êtes connecté au noeud\n${endPointGVA.split('/')[2]}"; _message = "Vous êtes connecté au noeud\n${SubstrateSdk().subNode}";
} }
final snackBar = SnackBar( final snackBar = SnackBar(
content: Text(_message), duration: const Duration(seconds: 2)); content: Text(_message), duration: const Duration(seconds: 2));

View File

@ -1,15 +1,20 @@
import 'dart:convert'; import 'dart:convert';
import 'dart:io';
import 'package:crypto/crypto.dart';
import 'package:fast_base58/fast_base58.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:gecko/globals.dart';
import 'package:polkawallet_sdk/api/apiKeyring.dart'; import 'package:polkawallet_sdk/api/apiKeyring.dart';
import 'package:polkawallet_sdk/api/types/networkParams.dart'; import 'package:polkawallet_sdk/api/types/networkParams.dart';
import 'package:polkawallet_sdk/api/types/txInfoData.dart';
import 'package:polkawallet_sdk/polkawallet_sdk.dart'; import 'package:polkawallet_sdk/polkawallet_sdk.dart';
import 'package:polkawallet_sdk/storage/keyring.dart'; import 'package:polkawallet_sdk/storage/keyring.dart';
import 'package:truncate/truncate.dart';
class SubstrateSdk with ChangeNotifier { class SubstrateSdk with ChangeNotifier {
final String subNode = '192.168.1.85:9944'; final List subNode = ['127.0.0.1:9944', '192.168.1.85:9944'];
final bool isSsl = false; final bool isSsl = false;
final int ss58 = 42;
final WalletSDK sdk = WalletSDK(); final WalletSDK sdk = WalletSDK();
final Keyring keyring = Keyring(); final Keyring keyring = Keyring();
@ -25,7 +30,8 @@ class SubstrateSdk with ChangeNotifier {
Future<void> initApi() async { Future<void> initApi() async {
sdkLoading = true; sdkLoading = true;
await keyring.init([0, 2]); await keyring.init([ss58]);
keyring.setSS58(ss58);
await sdk.init(keyring); await sdk.init(keyring);
sdkReady = true; sdkReady = true;
@ -35,14 +41,18 @@ class SubstrateSdk with ChangeNotifier {
Future<void> connectNode() async { Future<void> connectNode() async {
final String socketKind = isSsl ? 'wss' : 'ws'; final String socketKind = isSsl ? 'wss' : 'ws';
final node = NetworkParams(); List<NetworkParams> node = [];
node.name = 'pokaniter'; for (final sn in subNode) {
node.endpoint = '$socketKind://$subNode'; final n = NetworkParams();
node.ss58 = 42; n.name = 'duniter';
final res = await sdk.api.connectNode(keyring, [node]).timeout( n.endpoint = '$socketKind://$sn';
const Duration(seconds: 10), n.ss58 = ss58;
onTimeout: () => null, node.add(n);
); }
final res = await sdk.api.connectNode(keyring, node).timeout(
const Duration(seconds: 10),
onTimeout: () => null,
);
if (res != null) { if (res != null) {
nodeConnected = true; nodeConnected = true;
notifyListeners(); notifyListeners();
@ -55,7 +65,17 @@ class SubstrateSdk with ChangeNotifier {
}); });
} }
Future<void> importFromKeystore() async { Future<bool> importAccount({bool fromMnemonic = false}) async {
final KeyType keytype;
final String keyToImport;
if (fromMnemonic) {
keytype = KeyType.mnemonic;
keyToImport = generatedMnemonic;
} else {
keytype = KeyType.keystore;
keyToImport = jsonKeystore.text.replaceAll("'", "\\'");
}
importIsLoading = true; importIsLoading = true;
notifyListeners(); notifyListeners();
final clipboardData = await Clipboard.getData(Clipboard.kTextPlain); final clipboardData = await Clipboard.getData(Clipboard.kTextPlain);
@ -63,8 +83,8 @@ class SubstrateSdk with ChangeNotifier {
final json = await sdk.api.keyring final json = await sdk.api.keyring
.importAccount( .importAccount(
keyring, keyring,
keyType: KeyType.keystore, keyType: keytype,
key: jsonKeystore.text.replaceAll("'", "\\'"), key: keyToImport,
name: 'testKey', name: 'testKey',
password: keystorePassword.text, password: keystorePassword.text,
) )
@ -72,44 +92,119 @@ class SubstrateSdk with ChangeNotifier {
importIsLoading = false; importIsLoading = false;
notifyListeners(); notifyListeners();
}); });
final acc = await sdk.api.keyring if (json == null) return false;
.addAccount( try {
keyring, final acc = await sdk.api.keyring.addAccount(
keyType: KeyType.mnemonic, keyring,
acc: json!, keyType: KeyType.mnemonic,
password: keystorePassword.text, acc: json,
) password: keystorePassword.text,
.catchError((e) { );
Clipboard.setData(ClipboardData(text: jsonEncode(acc.toJson())));
} catch (e) {
importIsLoading = false; importIsLoading = false;
notifyListeners(); notifyListeners();
}); }
// await keystoreBox.clear(); // await keystoreBox.clear();
await keystoreBox.add(acc.toJson()); // await keystoreBox.add(acc.toJson());
Clipboard.setData(ClipboardData(text: jsonEncode(acc.toJson())));
importIsLoading = false; importIsLoading = false;
await Future.delayed(const Duration(milliseconds: 20));
notifyListeners(); notifyListeners();
return true;
} }
void reload() { void reload() {
notifyListeners(); notifyListeners();
} }
List getKeyStoreAddress() { Future<List<AddressInfo>> getKeyStoreAddress() async {
List result = []; List<AddressInfo> result = [];
for (var element in keystoreBox.values) { // sdk.api.account.unsubscribeBalance();
for (var element in keyring.allAccounts) {
// Clipboard.setData(ClipboardData(text: jsonEncode(element))); // Clipboard.setData(ClipboardData(text: jsonEncode(element)));
result.add(element['address']); final account = AddressInfo(address: element.address);
// await sdk.api.account.subscribeBalance(element.address, (p0) {
// account.balance = int.parse(p0.freeBalance) / 100;
// });
// sdk.api.setting.unsubscribeBestNumber();
if (nodeConnected) {
final brutBalance = await sdk.api.account.queryBalance(element.address);
account.balance = int.parse(brutBalance!.freeBalance) / 100;
}
result.add(account);
} }
return result; return result;
} }
Future<String> generateMnemonic() async { Future<void> deleteAllAccounts() async {
final gen = await sdk.api.keyring.generateMnemonic(42); for (var account in keyring.allAccounts) {
await sdk.api.keyring.deleteAccount(keyring, account);
}
}
Future<bool> generateMnemonic() async {
final gen = await sdk.api.keyring.generateMnemonic(ss58);
generatedMnemonic = gen.mnemonic!; generatedMnemonic = gen.mnemonic!;
notifyListeners();
return gen.mnemonic!; final res = await importAccount(fromMnemonic: true);
return res;
}
pay(BuildContext context, String address, double amount,
String password) async {
final sender = TxSenderData(
keyring.current.address,
keyring.current.pubKey,
);
final txInfo = TxInfoData('balances', 'transfer', sender);
try {
final hash = await sdk.api.tx.signAndSend(
txInfo,
[address, amount * 100],
password,
onStatusChange: (status) {
print('status: ' + status);
if (status == 'Ready') {
snack(context, 'Paiement effectué avec succès !');
}
},
);
print(hash.toString());
} catch (err) {
print(err.toString());
}
} }
} }
void snack(BuildContext context, String message, {int duration = 2}) {
final snackBar =
SnackBar(content: Text(message), duration: Duration(seconds: duration));
ScaffoldMessenger.of(context).showSnackBar(snackBar);
}
class AddressInfo {
final String? address;
double balance;
AddressInfo({@required this.address, this.balance = 0});
}
String getShortPubkey(String pubkey) {
List<int> pubkeyByte = Base58Decode(pubkey);
Digest pubkeyS256 = sha256.convert(sha256.convert(pubkeyByte).bytes);
String pubkeyCheksum = Base58Encode(pubkeyS256.bytes);
String pubkeyChecksumShort =
truncate(pubkeyCheksum, 3, omission: "", position: TruncatePosition.end);
String pubkeyShort = truncate(pubkey, 5,
omission: String.fromCharCode(0x2026),
position: TruncatePosition.end) +
truncate(pubkey, 4, omission: "", position: TruncatePosition.start) +
':$pubkeyChecksumShort';
return pubkeyShort;
}

View File

@ -52,30 +52,50 @@ class SubstrateSandBox extends StatelessWidget {
height: 35, height: 35,
), ),
onTap: () async { onTap: () async {
await keystoreBox.clear(); await _sub.deleteAllAccounts();
_sub.reload(); _sub.reload();
}, },
), ),
const SizedBox(width: 10), const SizedBox(width: 10),
]), ]),
FutureBuilder(
Text(keystoreBox.isEmpty future: _sub.getKeyStoreAddress(),
? '-' builder: (BuildContext context,
: _sub.getKeyStoreAddress().toString()), AsyncSnapshot<List<AddressInfo>> _data) {
// const SizedBox(height: 40), return Column(children: [
// const Text('Trousseau:'), for (final AddressInfo e in _data.data!)
// TextField( Row(children: [
// controller: _sub.jsonKeystore, InkWell(
// onChanged: (_) => _sub.reload(), onTap: () => _sub.keyring.setCurrent(_sub
// minLines: 5, .keyring.keyPairs
// maxLines: 5, .firstWhere((element) =>
// ), element.address == e.address!)),
child: Text(
getShortPubkey(e.address!),
style: const TextStyle(
fontFamily: 'Monospace'),
),
),
const SizedBox(width: 20),
InkWell(
onTap: () async => await _sub.pay(
context,
e.address!,
10,
_sub.keystorePassword.text),
child: Text("${e.balance.toString()} ğdev"),
)
])
]);
}),
const SizedBox(height: 20), const SizedBox(height: 20),
const Text('Mot de passe du trousseau:'), const Text('Mot de passe du trousseau:'),
TextField( TextField(
controller: _sub.keystorePassword, controller: _sub.keystorePassword,
obscureText: true, obscureText: true,
obscuringCharacter: '', obscuringCharacter: '',
enableSuggestions: false,
autocorrect: false,
onChanged: (_) => _sub.reload(), onChanged: (_) => _sub.reload(),
), ),
Column( Column(
@ -89,9 +109,14 @@ class SubstrateSandBox extends StatelessWidget {
), ),
onPressed: _sub.keystorePassword.text.isNotEmpty onPressed: _sub.keystorePassword.text.isNotEmpty
? () async { ? () async {
await _sub.importFromKeystore(); final res = await _sub.importAccount();
_sub.importIsLoading = false; _sub.importIsLoading = false;
_sub.reload(); _sub.reload();
snack(
context,
res
? 'Portefeuille importé'
: 'Le format de trousseau est invalide');
} }
: null, : null,
child: const Text( child: const Text(
@ -108,10 +133,17 @@ class SubstrateSandBox extends StatelessWidget {
onPrimary: Colors.black, // foreground onPrimary: Colors.black, // foreground
), ),
onPressed: () async { onPressed: () async {
await _sub.generateMnemonic(); final res = await _sub.generateMnemonic();
_sub.importIsLoading = false;
_sub.reload();
snack(
context,
res
? 'Portefeuille importé'
: 'Le format de trousseau est invalide');
}, },
child: const Text( child: const Text(
'Générer un mnemonic', "Générer un mnemonic et l'importer",
style: TextStyle(fontSize: 20), style: TextStyle(fontSize: 20),
), ),
), ),

View File

@ -5,7 +5,7 @@ description: Pay with G1.
# pub.dev using `pub publish`. This is preferred for private packages. # 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 publish_to: 'none' # Remove this line if you wish to publish to pub.dev
version: 0.0.4+8 version: 0.0.5+1
environment: environment:
sdk: '>=2.12.0 <3.0.0' sdk: '>=2.12.0 <3.0.0'