Import mnemonic; can pay;
This commit is contained in:
parent
d2c89b1f14
commit
fa31a2d4d8
|
@ -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";
|
||||||
|
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
@ -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));
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
|
@ -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),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -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'
|
||||||
|
|
Loading…
Reference in New Issue