Newer
Older
// ignore_for_file: avoid_print
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:gecko/models/chest_data.dart';
import 'package:gecko/models/wallet_data.dart';
import 'package:gecko/providers/home.dart';
import 'package:gecko/providers/my_wallets.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/storage/keyring.dart';
import 'package:polkawallet_sdk/storage/types/keyPairData.dart';
import 'package:provider/provider.dart';
// import 'package:web_socket_channel/io.dart';
class SubstrateSdk with ChangeNotifier {
final WalletSDK sdk = WalletSDK();
final Keyring keyring = Keyring();
bool sdkReady = false;
bool nodeConnected = false;
int blocNumber = 0;
bool isLoadingEndpoint = false;
TextEditingController jsonKeystore = TextEditingController();
TextEditingController keystorePassword = TextEditingController();
Future<void> initApi() async {
await sdk.init(keyring);
sdkReady = true;
notifyListeners();
}
Future<void> connectNode(BuildContext ctx) async {
HomeProvider _homeProvider = Provider.of<HomeProvider>(ctx, listen: false);
// var connectivityResult = await (Connectivity().checkConnectivity());
// if (connectivityResult == ConnectivityResult.mobile ||
// connectivityResult == ConnectivityResult.wifi) {
// _homeProvider.changeMessage("Vous n'êtes pas connecté à internet", 0);
// return;
// }
_homeProvider.changeMessage("connectionPending".tr(), 0);
for (String _endpoint in configBox.get('endpoint')) {
final n = NetworkParams();
n.name = currencyName;
n.endpoint = _endpoint;
n.ss58 = ss58;
node.add(n);
}
// if (n.endpoint!.startsWith('ws://')) {
// timeout = 5000;
// }
//// Check websocket conenction - only for wss
// final channel = IOWebSocketChannel.connect(
// Uri.parse('wss://192.168.1.72:9944'),
// );
// channel.stream.listen(
// (dynamic message) {
// log.d('message $message');
// },
// onDone: () {
// log.d('ws channel closed');
// },
// onError: (error) {
// log.d('ws error $error');
// },
// );
if (sdk.api.connectedNode?.endpoint != null) {
await sdk.api.setting.unsubscribeBestNumber();
isLoadingEndpoint = true;
notifyListeners();
final res = await sdk.api.connectNode(keyring, node).timeout(
isLoadingEndpoint = false;
notifyListeners();
if (res != null) {
nodeConnected = true;
// Subscribe bloc number
sdk.api.setting.subscribeBestNumber((res) {
blocNumber = int.parse(res.toString());
// log.d(sdk.api.connectedNode?.endpoint);
nodeConnected = false;
_homeProvider.changeMessage("networkLost".tr(), 0);
} else {
nodeConnected = true;
notifyListeners();
"wellConnectedToNode".tr(args: [getConnectedEndpoint()!.split('/')[2]]),
} else {
nodeConnected = false;
_homeProvider.changeMessage("noDuniterEndointAvailable".tr(), 0);
}
log.d(sdk.api.connectedNode?.endpoint);
Future<String> importAccount(
{String mnemonic = '',
bool fromMnemonic = false,
String derivePath = '',
String password = ''}) async {
// toy exercise immense month enter answer table prefer speed cycle gold phone
final clipboardData = await Clipboard.getData(Clipboard.kTextPlain);
if (mnemonic != '') {
fromMnemonic = true;
generatedMnemonic = mnemonic;
} else if (clipboardData!.text!.split(' ').length == 12) {
fromMnemonic = true;
generatedMnemonic = clipboardData.text!;
}
if (password == '') {
password = keystorePassword.text;
}
final KeyType keytype;
final String keyToImport;
if (fromMnemonic) {
keytype = KeyType.mnemonic;
keyToImport = generatedMnemonic;
} else {
keytype = KeyType.keystore;
keyToImport = jsonKeystore.text.replaceAll("'", "\\'");
}
if (clipboardData?.text != null) jsonKeystore.text = clipboardData!.text!;
var json = await sdk.api.keyring
.importAccount(keyring,
keyType: keytype,
key: keyToImport,
name: derivePath,
password: password,
derivePath: derivePath,
cryptoType: CryptoType.sr25519)
.catchError((e) {
importIsLoading = false;
notifyListeners();
});
// Clipboard.setData(ClipboardData(text: jsonEncode(acc.toJson())));
return keyring.allAccounts.last.address!;
Future<List<AddressInfo>> getKeyStoreAddress() async {
List<AddressInfo> result = [];
// sdk.api.account.unsubscribeBalance();
for (var element in keyring.allAccounts) {
// Clipboard.setData(ClipboardData(text: jsonEncode(element)));
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();
account.balance = await getBalance(element.address!);
Future<List<int>> getCerts(String address) async {
final idtyIndex = await sdk.webView!
.evalJavascript('api.query.identity.identityIndexOf("$address")');
final _certsReceiver = await sdk.webView!
.evalJavascript('api.query.cert.storageIdtyCertMeta($idtyIndex)') ??
[];
return [_certsReceiver['receivedCount'], _certsReceiver['issuedCount']];
}
Future<Map> getCertData(String from, String to) async {
final idtyIndexFrom = await sdk.webView!
.evalJavascript('api.query.identity.identityIndexOf("$from")');
final idtyIndexTo = await sdk.webView!
.evalJavascript('api.query.identity.identityIndexOf("$to")');
final _certData = await sdk.webView!.evalJavascript(
'api.query.cert.storageCertsByIssuer($idtyIndexFrom, $idtyIndexTo)') ??
'';
if (_certData == '') return {};
return _certData;
}
Future<bool> hasAccountConsumers(String address) async {
final _accountInfo = await sdk.webView!
.evalJavascript('api.query.system.account("$address")');
final _consumers = _accountInfo['consumers'];
// log.d('Consumers: $_consumers');
return _consumers == 0 ? false : true;
}
Future<double> getBalance(String address, {bool isUd = false}) async {
double balance = 0.0;
// log.d('nodeConnected: ' + nodeConnected.toString());
if (nodeConnected) {
final brutBalance = await sdk.api.account.queryBalance(address);
balance = int.parse(brutBalance!.freeBalance) / 100;
} else {
balance = -1;
Future<double> subscribeBalance(String address, {bool isUd = false}) async {
double balance = 0.0;
if (nodeConnected) {
await sdk.api.account.subscribeBalance(address, (_balance) {
balance = int.parse(_balance.freeBalance) / 100;
notifyListeners();
});
}
return balance;
}
KeyPairData getKeypair(String address) {
return keyring.keyPairs.firstWhere((kp) => kp.address == address,
orElse: (() => KeyPairData()));
}
Future<bool> checkPassword(String address, String pass) async {
final account = getKeypair(address);

poka
committed
// log.d(account.address);
return await sdk.api.keyring.checkPassword(account, pass);
}
Future<String> getSeed(String address, String _pin) async {
final account = getKeypair(address);
keyring.setCurrent(account);
final _seed = await sdk.api.keyring.getDecryptedSeed(keyring, _pin);
String _seedText;
if (_seed == null) {
_seedText = '';
} else {
_seedText = _seed.seed!.split('//')[0];
}
log.d(_seedText);
return _seedText;
}
int getDerivationNumber(String address) {
final account = getKeypair(address);
final deriveNbr = account.name!.split('//')[1];
return int.parse(deriveNbr);
}
Future<KeyPairData?> changePassword(BuildContext context, String address,
String passOld, String? passNew) async {
final account = getKeypair(address);
MyWalletsProvider _myWalletProvider =
Provider.of<MyWalletsProvider>(context, listen: false);
_myWalletProvider.resetPinCode();
return await sdk.api.keyring.changePassword(keyring, passOld, passNew);
}
Future<void> deleteAllAccounts() async {
for (var account in keyring.allAccounts) {
await sdk.api.keyring.deleteAccount(keyring, account);
}
}
Future<void> deleteAccounts(List<String> address) async {
for (var a in address) {
final account = getKeypair(a);
await sdk.api.keyring.deleteAccount(keyring, account);
}
}
Future<String> generateMnemonic({String lang = appLang}) async {
final gen = await sdk.api.keyring.generateMnemonic(ss58);
// await Clipboard.setData(ClipboardData(text: generatedMnemonic));
Future<String> setCurrentWallet(WalletData _wallet) async {
final currentChestNumber = configBox.get('currentChest');
ChestData _newChestData = chestBox.get(currentChestNumber)!;
_newChestData.defaultWallet = _wallet.number;
await chestBox.put(currentChestNumber, _newChestData);
keyring.setCurrent(acc);
return acc.address!;
} catch (e) {
return (e.toString());
}
}
KeyPairData getCurrentWallet() {
try {
final acc = keyring.current;
return acc;
} catch (e) {
return KeyPairData();
}
}
{required String fromAddress,
required String destAddress,
required double amount,
required String password}) async {
log.d(keyring.current.address);
log.d(fromAddress);
log.d(password);
// log.d(await checkPassword(fromAddress, password));
final fromPubkey = await sdk.api.account.decodeAddress([fromAddress]);
log.d(fromPubkey!.keys.first);
fromAddress,
fromPubkey.keys.first,
final txInfo = TxInfoData(
'balances', amount == -1 ? 'transferAll' : 'transferKeepAlive', sender);
try {
final hash = await sdk.api.tx.signAndSend(
txInfo,
[destAddress, amount == -1 ? false : (amount * 100)],
).timeout(
const Duration(seconds: 12),
onTimeout: () => {},
log.d(hash.toString());
if (hash.isEmpty) {
transactionStatus = 'timeout';
notifyListeners();
return 'timeout';
} else {
transactionStatus = hash.toString();
notifyListeners();
return hash.toString();
}
} catch (e) {
transactionStatus = e.toString();
notifyListeners();
return e.toString();
}
}
Future<String> certify(
String fromAddress, String password, String toAddress) async {
transactionStatus = '';
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
log.d('me: ' + fromAddress);
log.d('to: ' + toAddress);
final _myIdtyStatus = await idtyStatus(fromAddress);
final _toIdtyStatus = await idtyStatus(toAddress);
log.d(_myIdtyStatus);
log.d(_toIdtyStatus);
if (_myIdtyStatus != 'Validated') {
transactionStatus = 'notMember';
notifyListeners();
return 'notMember';
}
final sender = TxSenderData(
keyring.current.address,
keyring.current.pubKey,
);
TxInfoData txInfo;
if (_toIdtyStatus == 'noid') {
txInfo = TxInfoData(
'identity',
'createIdentity',
sender,
);
} else if (_toIdtyStatus == 'Validated' ||
_toIdtyStatus == 'ConfirmedByOwner') {
txInfo = TxInfoData(
'cert',
'addCert',
sender,
);
} else {
transactionStatus = 'cantBeCert';
notifyListeners();
return 'cantBeCert';
}
log.d('Cert action: ' + txInfo.call!);
try {
final hash = await sdk.api.tx
.signAndSend(
txInfo,
[toAddress],
password,
)
.timeout(
const Duration(seconds: 12),
onTimeout: () => {},
);
log.d(hash);
if (hash.isEmpty) {
transactionStatus = 'timeout';
notifyListeners();
return 'timeout';
} else {
transactionStatus = hash.toString();
notifyListeners();
return hash.toString();
}
transactionStatus = e.toString();
notifyListeners();
Future<String> idtyStatus(String address, [bool smooth = true]) async {
// var tata = await sdk.webView!
// .evalJavascript('api.query.system.account("$address")');
var idtyIndex = await sdk.webView!
.evalJavascript('api.query.identity.identityIndexOf("$address")');
if (idtyIndex == null) {
return 'noid';
}
final idtyStatus = await sdk.webView!
.evalJavascript('api.query.identity.identities($idtyIndex)');
if (idtyStatus != null) {
final String _status = idtyStatus['status'];
return (_status);
} else {
return 'expired';
}
}
Future<String> confirmIdentity(
String fromAddress, String name, String password) async {
log.d('me: ' + keyring.current.address!);
final sender = TxSenderData(
keyring.current.address,
keyring.current.pubKey,
);
final txInfo = TxInfoData(
'identity',
'confirmIdentity',
sender,
);
try {
onStatusChange: (status) {
log.d('Transaction status: ' + status);
transactionStatus = status;
notifyListeners();
},
).timeout(
const Duration(seconds: 12),
onTimeout: () => {},
log.d(hash);
if (hash.isEmpty) {
transactionStatus = 'timeout';
notifyListeners();
return 'timeout';
} else {
transactionStatus = hash.toString();
notifyListeners();
return hash.toString();
}
transactionStatus = e.toString();
notifyListeners();
Future<bool> isMember(String address) async {
return await idtyStatus(address) == 'Validated';
}
// TODOO: Continue digging memberAddress detection
String memberAddress = '';
walletBox.toMap().forEach((key, value) async {
final bool _isMember = await isMember(value.address!);
log.d(_isMember);
if (_isMember) {
final currentChestNumber = configBox.get('currentChest');
ChestData _newChestData = chestBox.get(currentChestNumber)!;
_newChestData.memberWallet = value.number;
await chestBox.put(currentChestNumber, _newChestData);
memberAddress = value.address!;
return;
}
});
log.d(memberAddress);
return memberAddress;
}
Future<Map<String, int>> certState(String from, String to) async {
if (from != to && await isMember(from)) {
final _certData = await getCertData(from, to);
final _certMeta = await getCertMeta(from);
final int _removableOn = _certData['removableOn'] ?? 0;
final int _renewableOn = _certData['renewableOn'] ?? 0;
final int _nextIssuableOn = _certMeta['nextIssuableOn'] ?? 0;
log.d(_renewableOn.toString() +
'\n' +
_removableOn.toString() +
'\n' +
_nextIssuableOn.toString());
if (_renewableOn != 0) {
final certRenewDuration = (_renewableOn - blocNumber) * 6;
_result.putIfAbsent('certRenewable', () => certRenewDuration);
return _result;
} else if (_nextIssuableOn > blocNumber) {
final certDelayDuration = (_nextIssuableOn - blocNumber) * 6;
_result.putIfAbsent('certDelay', () => certDelayDuration);
return _result;
} else {
_result.putIfAbsent('canCert', () => 0);
return _result;
// Future<String> certState(String from, String to) async {
// return '';
// }
Future<Map> getCertMeta(String address) async {
var idtyIndex = await sdk.webView!
.evalJavascript('api.query.identity.identityIndexOf("$address")');
final _certMeta = await sdk.webView!
.evalJavascript('api.query.cert.storageIdtyCertMeta($idtyIndex)') ??
'';
// if (_certMeta['nextIssuableOn'] != 0) return {};
log.d(_certMeta);
return _certMeta;
}
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
Future revokeIdentity(String address, String password) async {
final idtyIndex = await sdk.webView!
.evalJavascript('api.query.identity.identityIndexOf("$address")');
final sender = TxSenderData(
keyring.current.address,
keyring.current.pubKey,
);
log.d(sender.address);
TxInfoData txInfo;
txInfo = TxInfoData(
'membership',
'revokeMembership',
sender,
);
try {
final hash = await sdk.api.tx
.signAndSend(
txInfo,
[idtyIndex],
password,
)
.timeout(
const Duration(seconds: 12),
onTimeout: () => {},
);
log.d(hash);
if (hash.isEmpty) {
transactionStatus = 'timeout';
notifyListeners();
return 'timeout';
} else {
transactionStatus = hash.toString();
notifyListeners();
return hash.toString();
}
} catch (e) {
transactionStatus = e.toString();
notifyListeners();
return e.toString();
}
}
BuildContext context, String address, int number, String password) async {
final keypair = getKeypair(address);
final seedMap =
await keyring.store.getDecryptedSeed(keypair.pubKey, password);
if (seedMap?['type'] != 'mnemonic') return '';
final List seedList = seedMap!['seed'].split('//');

poka
committed
mnemonic: generatedMnemonic,
fromMnemonic: true,
derivePath: '//$number',
password: password);
}
Future<String> generateRootKeypair(String address, String password) async {
final keypair = getKeypair(address);
final seedMap =
await keyring.store.getDecryptedSeed(keypair.pubKey, password);
if (seedMap?['type'] != 'mnemonic') return '';
final List seedList = seedMap!['seed'].split('//');
generatedMnemonic = seedList[0];
return await importAccount(fromMnemonic: true, password: password);
Future<bool> isMnemonicValid(String mnemonic) async {
// Needed for bad encoding of UTF-8
mnemonic = mnemonic.replaceAll('é', 'é');
mnemonic = mnemonic.replaceAll('è', 'è');
return await sdk.api.keyring.checkMnemonicValid(mnemonic);
}
String? getConnectedEndpoint() {
return sdk.api.connectedNode?.endpoint;
}
}
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});
}
void snackNode(BuildContext context, bool isConnected) {
String _message;
if (!isConnected) {
_message =
"Aucun noeud Duniter disponible, veuillez réessayer ultérieurement:\n${configBox.get('endpoint').first}";
SubstrateSdk _sub = Provider.of<SubstrateSdk>(context, listen: false);
"Vous êtes connecté au noeud\n${_sub.getConnectedEndpoint()!.split('//')[1]}";
final snackBar = SnackBar(
padding: const EdgeInsets.all(20),
content: Text(_message, style: const TextStyle(fontSize: 16)),
ScaffoldMessenger.of(context).showSnackBar(snackBar);
}
omission: String.fromCharCode(0x2026),
position: TruncatePosition.end) +
truncate(pubkey, 6, omission: "", position: TruncatePosition.start);