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<int> getIdentityIndexOf(String address) async {
return await sdk.webView!
.evalJavascript('api.query.identity.identityIndexOf("$address")') ??
0;
}
final idtyIndex = await getIdentityIndexOf(address);
final certsReceiver = await sdk.webView!
.evalJavascript('api.query.cert.storageIdtyCertMeta($idtyIndex)') ??
[];
return [certsReceiver['receivedCount'], certsReceiver['issuedCount']];

poka
committed
Future<int> getCertValidityPeriod(String from, String to) async {
final idtyIndexFrom = await getIdentityIndexOf(from);
final idtyIndexTo = await getIdentityIndexOf(to);

poka
committed
if (idtyIndexFrom == 0 || idtyIndexTo == 0) return 0;

poka
committed
final List certData = await sdk.webView!
.evalJavascript('api.query.cert.certsByReceiver($idtyIndexTo)') ??
[];

poka
committed
if (certData.isEmpty) return 0;
for (List certInfo in certData) {
if (certInfo[0] == idtyIndexFrom) {
return certInfo[1];
}
}

poka
committed
return 0;
Future<Map<String, dynamic>> getParameters() async {
final currencyParameters = await sdk.webView!
.evalJavascript('api.query.parameters.parametersStorage()') ??
{};
return currencyParameters;
}
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);
final fromIndex = await getIdentityIndexOf(fromAddress);
final toIndex = await getIdentityIndexOf(toAddress);
log.d(myIdtyStatus);
log.d(toIdtyStatus);
transactionStatus = 'notMember';
notifyListeners();
return 'notMember';
}
final toCerts = await getCerts(toAddress);
final currencyParameters = await getParameters();
final sender = TxSenderData(
keyring.current.address,
keyring.current.pubKey,
);
TxInfoData txInfo;
txInfo = TxInfoData(
'identity',
'createIdentity',
sender,
);
} else if (toIdtyStatus == 'Validated' ||
toIdtyStatus == 'ConfirmedByOwner') {
if (toCerts[0] >= currencyParameters['wotMinCertForMembership'] &&
toIdtyStatus != 'Validated') {
log.d('Batch cert and membership validation');
txInfo = TxInfoData(
'utility',
'batchAll',
sender,
);
} else {
txInfo = TxInfoData(
'cert',
'addCert',
sender,
);
}
} else {
transactionStatus = 'cantBeCert';
notifyListeners();
return 'cantBeCert';
}
log.d('Cert action: ${txInfo.call!}');
List txOptions = [];
if (txInfo.call == 'batchAll') {
txOptions = [
'cert.addCert($fromIndex, $toIndex)',
'identity.validateIdentity($toIndex)'
];
} else if (txInfo.call == 'createIdentity') {
txOptions = [toAddress];
} else if (txInfo.call == 'addCert') {
txOptions = [fromIndex, toIndex];
} else {
log.e('TX call is unexpected');
return 'Ğecko says: TX call is unexpected';
}
final hash = await sdk.api.tx
.signAndSend(
txInfo,
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 getIdentityIndexOf(address);
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)) {

poka
committed
final removableOn = await getCertValidityPeriod(from, to);
final certMeta = await getCertMeta(from);
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 getIdentityIndexOf(address);
.evalJavascript('api.query.cert.storageIdtyCertMeta($idtyIndex)') ??
'';
Future revokeIdentity(String address, String password) async {
final idtyIndex = await getIdentityIndexOf(address);
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
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);