// ignore_for_file: avoid_print import 'package:crypto/crypto.dart'; import 'package:fast_base58/fast_base58.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:gecko/globals.dart'; import 'package:polkawallet_sdk/api/apiKeyring.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:truncate/truncate.dart'; class SubstrateSdk with ChangeNotifier { final int ss58 = 42; final WalletSDK sdk = WalletSDK(); final Keyring keyring = Keyring(); String generatedMnemonic = ''; bool sdkReady = false; bool sdkLoading = false; bool nodeConnected = false; bool importIsLoading = false; int blocNumber = 0; bool isLoadingEndpoint = false; TextEditingController jsonKeystore = TextEditingController(); TextEditingController keystorePassword = TextEditingController(); Future initApi() async { sdkLoading = true; await keyring.init([ss58]); keyring.setSS58(ss58); await sdk.init(keyring); sdkReady = true; sdkLoading = false; notifyListeners(); } Future connectNode(BuildContext ctx) async { List node = []; final n = NetworkParams(); n.name = currencyName; n.endpoint = configBox.get('endpoint'); n.ss58 = ss58; node.add(n); if (sdk.api.connectedNode?.endpoint != null) { await sdk.api.setting.unsubscribeBestNumber(); } isLoadingEndpoint = true; notifyListeners(); final res = await sdk.api.connectNode(keyring, node).timeout( const Duration(seconds: 7), onTimeout: () => null, ); isLoadingEndpoint = false; notifyListeners(); if (res != null) { nodeConnected = true; notifyListeners(); snackNode(ctx, true); } else { nodeConnected = false; notifyListeners(); snackNode(ctx, false); } // Subscribe bloc number if (nodeConnected) { sdk.api.setting.subscribeBestNumber((res) { blocNumber = int.parse(res.toString()); notifyListeners(); }); } log.d(sdk.api.connectedNode?.endpoint); } Future 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("'", "\\'"); } importIsLoading = true; notifyListeners(); 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(); }); if (json == null) return ''; print(json); try { await sdk.api.keyring.addAccount( keyring, keyType: keytype, acc: json, password: password, ); // Clipboard.setData(ClipboardData(text: jsonEncode(acc.toJson()))); } catch (e) { print(e); importIsLoading = false; notifyListeners(); } importIsLoading = false; await Future.delayed(const Duration(milliseconds: 20)); notifyListeners(); final bakedAddress = keyring.allAccounts.last.address; return bakedAddress!; } void reload() { notifyListeners(); } Future> getKeyStoreAddress() async { List 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!); result.add(account); } return result; } Future getBalance(String address, {bool isUd = false}) async { double balance = 0.0; if (nodeConnected) { final brutBalance = await sdk.api.account.queryBalance(address); balance = int.parse(brutBalance!.freeBalance) / 100; } return balance; } KeyPairData getKeypair(String address) { return keyring.keyPairs.firstWhere((kp) => kp.address == address, orElse: (() => KeyPairData())); } Future checkPassword(String address, String pass) async { final account = getKeypair(address); return await sdk.api.keyring.checkPassword(account, pass); } int getDerivationNumber(String address) { final account = getKeypair(address); final deriveNbr = account.name!.split('//')[1]; return int.parse(deriveNbr); } Future changePassword( String address, String passOld, String? passNew) async { final account = getKeypair(address); keyring.setCurrent(account); return await sdk.api.keyring.changePassword(keyring, passOld, passNew); } Future deleteAllAccounts() async { for (var account in keyring.allAccounts) { await sdk.api.keyring.deleteAccount(keyring, account); } } Future generateMnemonic({String lang = appLang}) async { final gen = await sdk.api.keyring.generateMnemonic(ss58); generatedMnemonic = gen.mnemonic!; // final res = await importAccount(fromMnemonic: true); await Clipboard.setData(ClipboardData(text: generatedMnemonic)); return gen.mnemonic!; } // Future 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, 'Transaction terminé'); // } // }, // ); // print(hash.toString()); // return true; // } catch (err) { // print(err.toString()); // return false; // } // } String setCurrentWallet(String address) { try { final acc = getKeypair(address); keyring.setCurrent(acc); return acc.address!; } catch (e) { return (e.toString()); } } KeyPairData getCurrentWallet() { try { final acc = keyring.current; return acc; } catch (e) { return KeyPairData(); } } Future pay(BuildContext context, {required String fromAddress, required String destAddress, required double amount, required String password}) async { setCurrentWallet(fromAddress); final sender = TxSenderData( keyring.current.address, keyring.current.pubKey, ); final txInfo = TxInfoData('balances', 'transfer', sender); try { final hash = await sdk.api.tx.signAndSend( txInfo, [destAddress, amount * 100], password, onStatusChange: (status) { print('status: ' + status); if (status == 'Ready') { snack(context, 'Transaction terminé'); } }, ); print(hash.toString()); return 'confirmed'; } catch (e) { return e.toString(); } } Future derive( BuildContext context, String address, int number, String password) async { final keypair = getKeypair(address); final seedMap = await keyring.store.getDecryptedSeed(keypair.pubKey, password); print(seedMap); if (seedMap?['type'] != 'mnemonic') return ''; final List seedList = seedMap!['seed'].split('//'); generatedMnemonic = seedList[0]; int sourceDerivation = -1; // To get derivation number of this account if (seedList.length > 1) { sourceDerivation = int.parse(seedList[1]); } print(generatedMnemonic); print(sourceDerivation); return await importAccount(fromMnemonic: true, derivePath: '//$number'); } } 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"; } else { _message = "Vous êtes connecté au noeud\n${configBox.get('endpoint').split('//')[1]}"; } final snackBar = SnackBar(content: Text(_message), duration: const Duration(seconds: 2)); ScaffoldMessenger.of(context).showSnackBar(snackBar); } String getShortPubkey(String pubkey) { List 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; }