Newer
Older
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);
// configBox.delete('customEndpoint');
final List<NetworkParams> listEndpoints =
configBox.containsKey('customEndpoint')
? [getDuniterCustomEndpoint()]
: getDuniterBootstrap();
// final nodes = getDuniterBootstrap();
// 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, listEndpoints).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);
List<NetworkParams> getDuniterBootstrap() {
List<NetworkParams> node = [];
for (String endpoint in configBox.get('endpoint')) {
n.ss58 = ss58;
node.add(n);
}
return node;
}
NetworkParams getDuniterCustomEndpoint() {
final nodeParams = NetworkParams();
nodeParams.name = currencyName;
nodeParams.endpoint = configBox.get('customEndpoint');
nodeParams.ss58 = ss58;
return nodeParams;
}
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")');
if (idtyIndexFrom == null || idtyIndexTo == null) return {};
final certData = await sdk.webView!.evalJavascript(
'api.query.cert.storageCertsByIssuer($idtyIndexFrom, $idtyIndexTo)') ??
'';
Future<bool> hasAccountConsumers(String address) async {
final accountInfo = await sdk.webView!
.evalJavascript('api.query.system.account("$address")');
final consumers = accountInfo['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, (balanceData) {
balance = int.parse(balanceData.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 = '';
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);
Provider.of<MyWalletsProvider>(context, listen: false);
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 {
ChestData newChestData = chestBox.get(currentChestNumber)!;
newChestData.defaultWallet = wallet.number;
await chestBox.put(currentChestNumber, newChestData);
final acc = getKeypair(wallet.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();
}
}
{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);
final int amountUnit = (amount * 100).toInt();
try {
final hash = await sdk.api.tx.signAndSend(
txInfo,
[destAddress, amount == -1 ? false : amountUnit],
log.d('Transaction status: $status');
).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 = '';
log.d('me: $fromAddress');
log.d('to: $toAddress');
final myIdtyStatus = await idtyStatus(fromAddress);
final toIdtyStatus = await idtyStatus(toAddress);
log.d(myIdtyStatus);
log.d(toIdtyStatus);
transactionStatus = 'notMember';
notifyListeners();
return 'notMember';
}
final sender = TxSenderData(
keyring.current.address,
keyring.current.pubKey,
);
TxInfoData txInfo;
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'];
// log.d('Status $address: $_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 {
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> isMemberGet(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 isMemberGet(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 {
Map<String, int> result = {};
if (from != to && await isMemberGet(from)) {
final certData = await getCertData(from, to);
final certMeta = await getCertMeta(from);
final int removableOn = certData['removableOn'] ?? 0;
final int nextIssuableOn = certMeta['nextIssuableOn'] ?? 0;
final certRemovableDuration = (removableOn - blocNumber) * 6;
const int renewDelay = 2 * 30 * 24 * 3600; // 2 months
if (certRemovableDuration >= renewDelay) {
final certRenewDuration = certRemovableDuration - renewDelay;
result.putIfAbsent('certRenewable', () => certRenewDuration);
} else if (nextIssuableOn > blocNumber) {
final certDelayDuration = (nextIssuableOn - blocNumber) * 6;
result.putIfAbsent('certDelay', () => certDelayDuration);
result.putIfAbsent('canCert', () => 0);
}
Future<Map> getCertMeta(String address) async {
var idtyIndex = await sdk.webView!
.evalJavascript('api.query.identity.identityIndexOf("$address")');
.evalJavascript('api.query.cert.storageIdtyCertMeta($idtyIndex)') ??
'';
// if (_certMeta['nextIssuableOn'] != 0) return {};
// log.d(_certMeta);
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
698
699
700
701
702
703
704
705
706
707
708
709
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) {
message =
"${"noDuniterNodeAvailableTryLater".tr()}:\n${configBox.get('endpoint').first}";
SubstrateSdk sub = Provider.of<SubstrateSdk>(context, listen: false);
message =
"${"youAreConnectedToNode".tr()}\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);