2022-08-18 01:31:47 +02:00
|
|
|
// ignore_for_file: use_build_context_synchronously
|
|
|
|
|
2022-08-16 01:44:45 +02:00
|
|
|
import 'dart:typed_data';
|
2022-06-17 01:13:14 +02:00
|
|
|
import 'package:easy_localization/easy_localization.dart';
|
2022-02-18 00:20:21 +01:00
|
|
|
import 'package:flutter/material.dart';
|
2022-05-19 07:00:25 +02:00
|
|
|
import 'package:gecko/globals.dart';
|
2022-05-28 21:15:47 +02:00
|
|
|
import 'package:gecko/models/chest_data.dart';
|
|
|
|
import 'package:gecko/models/wallet_data.dart';
|
2022-05-29 04:30:03 +02:00
|
|
|
import 'package:gecko/providers/home.dart';
|
2022-05-31 18:23:56 +02:00
|
|
|
import 'package:gecko/providers/my_wallets.dart';
|
2022-02-18 02:19:08 +01:00
|
|
|
import 'package:polkawallet_sdk/api/apiKeyring.dart';
|
2022-02-18 00:20:21 +01:00
|
|
|
import 'package:polkawallet_sdk/api/types/networkParams.dart';
|
2022-02-19 23:51:12 +01:00
|
|
|
import 'package:polkawallet_sdk/api/types/txInfoData.dart';
|
2022-02-18 00:20:21 +01:00
|
|
|
import 'package:polkawallet_sdk/polkawallet_sdk.dart';
|
|
|
|
import 'package:polkawallet_sdk/storage/keyring.dart';
|
2022-05-04 19:00:09 +02:00
|
|
|
import 'package:polkawallet_sdk/storage/types/keyPairData.dart';
|
2022-05-27 06:11:09 +02:00
|
|
|
import 'package:provider/provider.dart';
|
2022-02-19 23:51:12 +01:00
|
|
|
import 'package:truncate/truncate.dart';
|
2022-08-16 01:44:45 +02:00
|
|
|
import 'package:pointycastle/pointycastle.dart' as pc;
|
|
|
|
import "package:hex/hex.dart";
|
2022-02-18 00:20:21 +01:00
|
|
|
|
|
|
|
class SubstrateSdk with ChangeNotifier {
|
|
|
|
final WalletSDK sdk = WalletSDK();
|
|
|
|
final Keyring keyring = Keyring();
|
2022-02-18 19:49:37 +01:00
|
|
|
String generatedMnemonic = '';
|
2022-02-18 00:20:21 +01:00
|
|
|
bool sdkReady = false;
|
2022-02-18 18:57:03 +01:00
|
|
|
bool sdkLoading = false;
|
2022-02-18 00:20:21 +01:00
|
|
|
bool nodeConnected = false;
|
2022-02-18 19:49:37 +01:00
|
|
|
bool importIsLoading = false;
|
2022-02-18 00:20:21 +01:00
|
|
|
int blocNumber = 0;
|
2022-05-20 15:15:29 +02:00
|
|
|
bool isLoadingEndpoint = false;
|
2022-05-25 20:40:55 +02:00
|
|
|
String debugConnection = '';
|
2022-05-26 02:19:29 +02:00
|
|
|
String transactionStatus = '';
|
2022-08-17 17:50:05 +02:00
|
|
|
final int initSs58 = 42;
|
|
|
|
Map<String, int> currencyParameters = {};
|
2022-08-18 01:31:47 +02:00
|
|
|
TextEditingController csSalt = TextEditingController();
|
|
|
|
TextEditingController csPassword = TextEditingController();
|
|
|
|
String g1V1NewAddress = '';
|
|
|
|
bool isCesiumIDVisible = true;
|
2022-02-18 00:20:21 +01:00
|
|
|
|
2022-08-11 15:47:08 +02:00
|
|
|
/////////////////////////////////////
|
|
|
|
////////// 1: API METHODS ///////////
|
|
|
|
/////////////////////////////////////
|
|
|
|
|
2022-08-12 10:04:55 +02:00
|
|
|
Future<String> executeCall(TxInfoData txInfo, txOptions, String password,
|
|
|
|
[String? rawParams]) async {
|
2022-08-11 15:47:08 +02:00
|
|
|
try {
|
|
|
|
final hash = await sdk.api.tx
|
2022-08-12 10:04:55 +02:00
|
|
|
.signAndSend(txInfo, txOptions, password, rawParam: rawParams)
|
2022-08-11 15:47:08 +02:00
|
|
|
.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();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-09 11:26:45 +02:00
|
|
|
Future getStorage(String call) async {
|
|
|
|
return await sdk.webView!.evalJavascript('api.query.$call');
|
|
|
|
}
|
|
|
|
|
2022-08-17 17:50:05 +02:00
|
|
|
Future getStorageConst(String call) async {
|
|
|
|
return (await sdk.webView!
|
2022-08-17 23:01:20 +02:00
|
|
|
.evalJavascript('api.consts.$call', wrapPromise: false) ??
|
|
|
|
[null])[0];
|
2022-08-17 17:50:05 +02:00
|
|
|
}
|
|
|
|
|
2022-08-11 15:47:08 +02:00
|
|
|
TxSenderData _setSender() {
|
|
|
|
return TxSenderData(
|
|
|
|
keyring.current.address,
|
|
|
|
keyring.current.pubKey,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////
|
|
|
|
////////// 2: GET ONCHAIN STORAGE //////////
|
|
|
|
////////////////////////////////////////////
|
|
|
|
|
|
|
|
Future<int> getIdentityIndexOf(String address) async {
|
|
|
|
return await getStorage('identity.identityIndexOf("$address")') ?? 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<List<int>> getCerts(String address) async {
|
|
|
|
final idtyIndex = await getIdentityIndexOf(address);
|
|
|
|
final certsReceiver =
|
|
|
|
await getStorage('cert.storageIdtyCertMeta($idtyIndex)') ?? [];
|
|
|
|
|
|
|
|
return [certsReceiver['receivedCount'], certsReceiver['issuedCount']];
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<int> getCertValidityPeriod(String from, String to) async {
|
|
|
|
final idtyIndexFrom = await getIdentityIndexOf(from);
|
|
|
|
final idtyIndexTo = await getIdentityIndexOf(to);
|
|
|
|
|
|
|
|
if (idtyIndexFrom == 0 || idtyIndexTo == 0) return 0;
|
|
|
|
|
|
|
|
final List certData =
|
|
|
|
await getStorage('cert.certsByReceiver($idtyIndexTo)') ?? [];
|
|
|
|
|
|
|
|
if (certData.isEmpty) return 0;
|
|
|
|
for (List certInfo in certData) {
|
|
|
|
if (certInfo[0] == idtyIndexFrom) {
|
|
|
|
return certInfo[1];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<bool> hasAccountConsumers(String address) async {
|
|
|
|
final accountInfo = await getStorage('system.account("$address")');
|
|
|
|
final consumers = accountInfo['consumers'];
|
|
|
|
return consumers == 0 ? false : true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Future<double> getBalance(String address) async {
|
|
|
|
// double balance = 0.0;
|
|
|
|
|
|
|
|
// if (nodeConnected) {
|
|
|
|
// final brutBalance = await sdk.api.account.queryBalance(address);
|
|
|
|
// // log.d(brutBalance?.toJson());
|
|
|
|
// balance = int.parse(brutBalance!.freeBalance) / 100;
|
|
|
|
// } else {
|
|
|
|
// balance = -1;
|
|
|
|
// }
|
|
|
|
|
|
|
|
// await getUnclaimedUd(address);
|
|
|
|
// return balance;
|
|
|
|
// }
|
|
|
|
|
2022-08-11 19:19:50 +02:00
|
|
|
Future<Map<String, double>> getBalance(String address) async {
|
2022-08-17 17:50:05 +02:00
|
|
|
// log.d('currencyParameters: $currencyParameters');
|
|
|
|
|
2022-08-14 16:34:24 +02:00
|
|
|
if (!nodeConnected) {
|
|
|
|
return {
|
|
|
|
'transferableBalance': 0,
|
|
|
|
'free': 0,
|
|
|
|
'unclaimedUds': 0,
|
|
|
|
'reserved': 0,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2022-08-11 15:47:08 +02:00
|
|
|
// Get onchain storage values
|
|
|
|
final Map balanceGlobal = await getStorage('system.account("$address")');
|
|
|
|
final int? idtyIndex =
|
|
|
|
await getStorage('identity.identityIndexOf("$address")');
|
|
|
|
final Map? idtyData = idtyIndex == null
|
|
|
|
? null
|
|
|
|
: await getStorage('identity.identities($idtyIndex)');
|
|
|
|
final int currentUdIndex =
|
|
|
|
int.parse(await getStorage('universalDividend.currentUdIndex()'));
|
|
|
|
final List pastReevals =
|
|
|
|
await getStorage('universalDividend.pastReevals()');
|
|
|
|
|
|
|
|
// Compute amount of claimable UDs
|
2022-08-11 19:19:50 +02:00
|
|
|
final int unclaimedUds = _computeUnclaimUds(currentUdIndex,
|
2022-08-11 15:47:08 +02:00
|
|
|
idtyData?['data']?['firstEligibleUd'] ?? 0, pastReevals);
|
|
|
|
|
|
|
|
// Calculate transferable and potential balance
|
|
|
|
final int transferableBalance =
|
2022-08-11 19:19:50 +02:00
|
|
|
(balanceGlobal['data']['free'] + unclaimedUds);
|
|
|
|
|
|
|
|
Map<String, double> finalBalances = {
|
|
|
|
'transferableBalance': transferableBalance / 100,
|
|
|
|
'free': balanceGlobal['data']['free'] / 100,
|
|
|
|
'unclaimedUds': unclaimedUds / 100,
|
|
|
|
'reserved': balanceGlobal['data']['reserved'] / 100,
|
|
|
|
};
|
2022-08-11 15:47:08 +02:00
|
|
|
|
2022-08-11 19:19:50 +02:00
|
|
|
// log.i(finalBalances);
|
2022-08-11 15:47:08 +02:00
|
|
|
|
2022-08-11 19:19:50 +02:00
|
|
|
return finalBalances;
|
2022-08-11 15:47:08 +02:00
|
|
|
}
|
|
|
|
|
2022-08-11 19:19:50 +02:00
|
|
|
int _computeUnclaimUds(
|
2022-08-11 15:47:08 +02:00
|
|
|
int currentUdIndex, int firstEligibleUd, List pastReevals) {
|
|
|
|
int totalAmount = 0;
|
|
|
|
|
|
|
|
if (firstEligibleUd == 0) return 0;
|
|
|
|
|
|
|
|
for (final List reval in pastReevals.reversed) {
|
|
|
|
final int revalNbr = reval[0];
|
|
|
|
final int revalValue = reval[1];
|
|
|
|
|
|
|
|
// Loop each UDs revaluations and sum unclaimed balance
|
|
|
|
if (revalNbr <= firstEligibleUd) {
|
|
|
|
final count = currentUdIndex - firstEligibleUd;
|
|
|
|
totalAmount += count * revalValue;
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
final count = currentUdIndex - revalNbr;
|
|
|
|
totalAmount += count * revalValue;
|
|
|
|
currentUdIndex = revalNbr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return totalAmount;
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<bool> isMemberGet(String address) async {
|
|
|
|
return await idtyStatus(address) == 'Validated';
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<Map<String, int>> certState(String from, String to) async {
|
|
|
|
Map<String, int> result = {};
|
|
|
|
if (from != to && await isMemberGet(from)) {
|
|
|
|
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);
|
|
|
|
} else {
|
|
|
|
result.putIfAbsent('canCert', () => 0);
|
|
|
|
}
|
|
|
|
}
|
2022-08-13 13:45:43 +02:00
|
|
|
|
2022-08-17 23:28:19 +02:00
|
|
|
final toStatus = await idtyStatus(to);
|
|
|
|
// log.d('certMeta: $toStatus');
|
|
|
|
|
|
|
|
if (toStatus == 'Created') result.putIfAbsent('toStatus', () => 1);
|
|
|
|
|
2022-08-11 15:47:08 +02:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<Map> getCertMeta(String address) async {
|
|
|
|
var idtyIndex = await getIdentityIndexOf(address);
|
|
|
|
|
|
|
|
final certMeta =
|
|
|
|
await getStorage('cert.storageIdtyCertMeta($idtyIndex)') ?? '';
|
|
|
|
|
|
|
|
return certMeta;
|
|
|
|
}
|
|
|
|
|
2022-08-18 14:26:12 +02:00
|
|
|
Future<String> idtyStatus(String address) async {
|
2022-08-11 15:47:08 +02:00
|
|
|
var idtyIndex = await getIdentityIndexOf(address);
|
|
|
|
|
|
|
|
if (idtyIndex == 0) {
|
|
|
|
return 'noid';
|
|
|
|
}
|
|
|
|
|
|
|
|
final idtyStatus = await getStorage('identity.identities($idtyIndex)');
|
|
|
|
|
|
|
|
if (idtyStatus != null) {
|
|
|
|
final String status = idtyStatus['status'];
|
|
|
|
|
|
|
|
return (status);
|
|
|
|
} else {
|
|
|
|
return 'expired';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-18 18:22:36 +02:00
|
|
|
Future<String> getGenesisHash() async {
|
|
|
|
final String genesisHash = await sdk.webView!.evalJavascript(
|
|
|
|
'api.genesisHash.toHex()',
|
|
|
|
wrapPromise: false,
|
|
|
|
) ??
|
|
|
|
'';
|
|
|
|
// log.d('genesisHash: $genesisHash');
|
|
|
|
return genesisHash;
|
|
|
|
}
|
|
|
|
|
|
|
|
Future addressToPubkey(String address) async {
|
|
|
|
await sdk.api.account.decodeAddress([address]);
|
|
|
|
}
|
2022-08-17 17:50:05 +02:00
|
|
|
|
|
|
|
// Future pubkeyToAddress(String pubkey) async {
|
|
|
|
// await sdk.api.account.encodeAddress([pubkey]);
|
|
|
|
// }
|
|
|
|
|
|
|
|
Future initCurrencyParameters() async {
|
|
|
|
currencyParameters['ss58'] =
|
|
|
|
await getStorageConst('system.ss58Prefix.words');
|
|
|
|
currencyParameters['minCertForMembership'] =
|
|
|
|
await getStorageConst('wot.minCertForMembership.words');
|
|
|
|
currencyParameters['newAccountPrice'] =
|
|
|
|
await getStorageConst('account.newAccountPrice.words');
|
|
|
|
currencyParameters['existentialDeposit'] =
|
|
|
|
await getStorageConst('balances.existentialDeposit.words');
|
|
|
|
currencyParameters['certPeriod'] =
|
|
|
|
await getStorageConst('cert.certPeriod.words');
|
|
|
|
currencyParameters['certMaxByIssuer'] =
|
|
|
|
await getStorageConst('cert.maxByIssuer.words');
|
|
|
|
currencyParameters['certValidityPeriod'] =
|
|
|
|
await getStorageConst('cert.validityPeriod.words');
|
|
|
|
|
|
|
|
log.i('currencyParameters: $currencyParameters');
|
|
|
|
}
|
2022-08-11 15:47:08 +02:00
|
|
|
|
2022-08-18 14:26:12 +02:00
|
|
|
void cesiumIDisVisible() {
|
|
|
|
isCesiumIDVisible = !isCesiumIDVisible;
|
|
|
|
notifyListeners();
|
|
|
|
}
|
|
|
|
|
2022-08-11 15:47:08 +02:00
|
|
|
/////////////////////////////////////
|
|
|
|
////// 3: SUBSTRATE CONNECTION //////
|
|
|
|
/////////////////////////////////////
|
|
|
|
|
2022-02-18 00:20:21 +01:00
|
|
|
Future<void> initApi() async {
|
2022-02-18 18:57:03 +01:00
|
|
|
sdkLoading = true;
|
2022-08-17 17:50:05 +02:00
|
|
|
await keyring.init([initSs58]);
|
|
|
|
keyring.setSS58(initSs58);
|
2022-02-18 00:20:21 +01:00
|
|
|
|
|
|
|
await sdk.init(keyring);
|
|
|
|
sdkReady = true;
|
2022-02-18 18:57:03 +01:00
|
|
|
sdkLoading = false;
|
2022-02-18 00:20:21 +01:00
|
|
|
notifyListeners();
|
|
|
|
}
|
|
|
|
|
2022-08-11 15:47:08 +02:00
|
|
|
String? getConnectedEndpoint() {
|
|
|
|
return sdk.api.connectedNode?.endpoint;
|
|
|
|
}
|
|
|
|
|
2022-05-20 15:15:29 +02:00
|
|
|
Future<void> connectNode(BuildContext ctx) async {
|
2022-08-06 02:22:41 +02:00
|
|
|
HomeProvider homeProvider = Provider.of<HomeProvider>(ctx, listen: false);
|
2022-08-17 23:01:20 +02:00
|
|
|
MyWalletsProvider myWalletProvider =
|
|
|
|
Provider.of<MyWalletsProvider>(ctx, listen: false);
|
2022-05-27 06:11:09 +02:00
|
|
|
|
2022-08-06 02:22:41 +02:00
|
|
|
homeProvider.changeMessage("connectionPending".tr(), 0);
|
2022-06-01 21:00:17 +02:00
|
|
|
|
2022-07-14 18:18:19 +02:00
|
|
|
// configBox.delete('customEndpoint');
|
|
|
|
final List<NetworkParams> listEndpoints =
|
|
|
|
configBox.containsKey('customEndpoint')
|
|
|
|
? [getDuniterCustomEndpoint()]
|
|
|
|
: getDuniterBootstrap();
|
|
|
|
|
2022-05-25 20:40:55 +02:00
|
|
|
int timeout = 10000;
|
2022-05-23 12:39:01 +02:00
|
|
|
|
2022-05-20 15:15:29 +02:00
|
|
|
if (sdk.api.connectedNode?.endpoint != null) {
|
|
|
|
await sdk.api.setting.unsubscribeBestNumber();
|
2022-02-19 23:51:12 +01:00
|
|
|
}
|
2022-05-20 15:15:29 +02:00
|
|
|
|
|
|
|
isLoadingEndpoint = true;
|
|
|
|
notifyListeners();
|
2022-07-14 18:18:19 +02:00
|
|
|
final res = await sdk.api.connectNode(keyring, listEndpoints).timeout(
|
2022-05-23 12:39:01 +02:00
|
|
|
Duration(milliseconds: timeout),
|
2022-02-19 23:51:12 +01:00
|
|
|
onTimeout: () => null,
|
|
|
|
);
|
2022-05-20 15:15:29 +02:00
|
|
|
isLoadingEndpoint = false;
|
|
|
|
notifyListeners();
|
2022-02-18 00:20:21 +01:00
|
|
|
if (res != null) {
|
|
|
|
nodeConnected = true;
|
2022-08-08 18:56:59 +02:00
|
|
|
// await getSs58Prefix();
|
2022-05-25 20:40:55 +02:00
|
|
|
|
|
|
|
// Subscribe bloc number
|
|
|
|
sdk.api.setting.subscribeBestNumber((res) {
|
|
|
|
blocNumber = int.parse(res.toString());
|
2022-06-12 18:03:17 +02:00
|
|
|
// log.d(sdk.api.connectedNode?.endpoint);
|
2022-06-01 21:00:17 +02:00
|
|
|
if (sdk.api.connectedNode?.endpoint == null) {
|
2022-06-12 18:03:17 +02:00
|
|
|
nodeConnected = false;
|
2022-08-06 02:22:41 +02:00
|
|
|
homeProvider.changeMessage("networkLost".tr(), 0);
|
2022-06-12 18:03:17 +02:00
|
|
|
} else {
|
|
|
|
nodeConnected = true;
|
2022-06-01 21:00:17 +02:00
|
|
|
}
|
2022-05-25 20:40:55 +02:00
|
|
|
notifyListeners();
|
|
|
|
});
|
2022-06-12 18:03:17 +02:00
|
|
|
|
2022-08-17 23:01:20 +02:00
|
|
|
await initCurrencyParameters();
|
2022-02-18 00:20:21 +01:00
|
|
|
notifyListeners();
|
2022-08-06 02:22:41 +02:00
|
|
|
homeProvider.changeMessage(
|
2022-06-17 20:18:54 +02:00
|
|
|
"wellConnectedToNode"
|
|
|
|
.tr(args: [getConnectedEndpoint()!.split('/')[2]]),
|
2022-06-01 21:00:17 +02:00
|
|
|
5);
|
2022-05-29 04:30:03 +02:00
|
|
|
// snackNode(ctx, true);
|
2022-05-20 15:15:29 +02:00
|
|
|
} else {
|
|
|
|
nodeConnected = false;
|
2022-05-25 20:40:55 +02:00
|
|
|
debugConnection = res.toString();
|
2022-05-20 15:15:29 +02:00
|
|
|
notifyListeners();
|
2022-08-06 02:22:41 +02:00
|
|
|
homeProvider.changeMessage("noDuniterEndointAvailable".tr(), 0);
|
2022-08-17 23:01:20 +02:00
|
|
|
if (!myWalletProvider.checkIfWalletExist()) snackNode(homeContext, false);
|
2022-02-18 00:20:21 +01:00
|
|
|
}
|
|
|
|
|
2022-05-20 15:15:29 +02:00
|
|
|
log.d(sdk.api.connectedNode?.endpoint);
|
2022-02-18 00:20:21 +01:00
|
|
|
}
|
2022-02-18 02:19:08 +01:00
|
|
|
|
2022-07-14 18:18:19 +02:00
|
|
|
List<NetworkParams> getDuniterBootstrap() {
|
|
|
|
List<NetworkParams> node = [];
|
|
|
|
|
2022-08-06 02:22:41 +02:00
|
|
|
for (String endpoint in configBox.get('endpoint')) {
|
2022-07-14 18:18:19 +02:00
|
|
|
final n = NetworkParams();
|
|
|
|
n.name = currencyName;
|
2022-08-06 02:22:41 +02:00
|
|
|
n.endpoint = endpoint;
|
2022-08-17 17:50:05 +02:00
|
|
|
n.ss58 = currencyParameters['ss58'] ?? initSs58;
|
2022-07-14 18:18:19 +02:00
|
|
|
node.add(n);
|
|
|
|
}
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
|
|
|
NetworkParams getDuniterCustomEndpoint() {
|
|
|
|
final nodeParams = NetworkParams();
|
|
|
|
nodeParams.name = currencyName;
|
|
|
|
nodeParams.endpoint = configBox.get('customEndpoint');
|
2022-08-17 17:50:05 +02:00
|
|
|
nodeParams.ss58 = currencyParameters['ss58'] ?? initSs58;
|
2022-07-14 18:18:19 +02:00
|
|
|
return nodeParams;
|
|
|
|
}
|
|
|
|
|
2022-05-04 19:00:09 +02:00
|
|
|
Future<String> importAccount(
|
|
|
|
{String mnemonic = '',
|
2022-05-19 07:00:25 +02:00
|
|
|
String derivePath = '',
|
2022-08-15 17:41:12 +02:00
|
|
|
required String password}) async {
|
|
|
|
const keytype = KeyType.mnemonic;
|
|
|
|
if (mnemonic != '') generatedMnemonic = mnemonic;
|
2022-02-19 23:51:12 +01:00
|
|
|
|
2022-02-18 19:49:37 +01:00
|
|
|
importIsLoading = true;
|
|
|
|
notifyListeners();
|
2022-08-15 17:41:12 +02:00
|
|
|
|
|
|
|
final json = await sdk.api.keyring
|
2022-02-20 20:49:05 +01:00
|
|
|
.importAccount(keyring,
|
|
|
|
keyType: keytype,
|
2022-08-15 17:41:12 +02:00
|
|
|
key: generatedMnemonic,
|
2022-05-19 07:00:25 +02:00
|
|
|
name: derivePath,
|
|
|
|
password: password,
|
2022-03-02 21:33:50 +01:00
|
|
|
derivePath: derivePath,
|
|
|
|
cryptoType: CryptoType.sr25519)
|
2022-02-18 19:49:37 +01:00
|
|
|
.catchError((e) {
|
|
|
|
importIsLoading = false;
|
|
|
|
notifyListeners();
|
|
|
|
});
|
2022-05-04 19:00:09 +02:00
|
|
|
if (json == null) return '';
|
2022-08-08 18:56:59 +02:00
|
|
|
// log.d(json);
|
2022-02-19 23:51:12 +01:00
|
|
|
try {
|
2022-05-19 07:00:25 +02:00
|
|
|
await sdk.api.keyring.addAccount(
|
2022-02-19 23:51:12 +01:00
|
|
|
keyring,
|
2022-02-22 10:39:45 +01:00
|
|
|
keyType: keytype,
|
2022-02-19 23:51:12 +01:00
|
|
|
acc: json,
|
2022-05-19 07:00:25 +02:00
|
|
|
password: password,
|
2022-02-19 23:51:12 +01:00
|
|
|
);
|
|
|
|
} catch (e) {
|
2022-08-06 02:22:41 +02:00
|
|
|
log.e(e);
|
2022-02-18 19:49:37 +01:00
|
|
|
importIsLoading = false;
|
|
|
|
notifyListeners();
|
2022-02-19 23:51:12 +01:00
|
|
|
}
|
2022-02-18 02:19:08 +01:00
|
|
|
|
2022-02-18 19:49:37 +01:00
|
|
|
importIsLoading = false;
|
2022-05-30 05:31:33 +02:00
|
|
|
|
2022-02-18 02:19:08 +01:00
|
|
|
notifyListeners();
|
2022-05-30 06:22:43 +02:00
|
|
|
return keyring.allAccounts.last.address!;
|
2022-02-18 02:19:08 +01:00
|
|
|
}
|
|
|
|
|
2022-08-11 15:47:08 +02:00
|
|
|
//////////////////////////////////
|
|
|
|
/////// 4: CRYPTOGRAPHY //////////
|
|
|
|
//////////////////////////////////
|
2022-05-28 21:15:47 +02:00
|
|
|
|
2022-05-19 07:00:25 +02:00
|
|
|
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);
|
2022-06-01 15:17:07 +02:00
|
|
|
|
2022-05-19 07:00:25 +02:00
|
|
|
return await sdk.api.keyring.checkPassword(account, pass);
|
|
|
|
}
|
|
|
|
|
2022-08-06 02:22:41 +02:00
|
|
|
Future<String> getSeed(String address, String pin) async {
|
2022-05-29 00:00:57 +02:00
|
|
|
final account = getKeypair(address);
|
|
|
|
keyring.setCurrent(account);
|
|
|
|
|
2022-08-06 02:22:41 +02:00
|
|
|
final seed = await sdk.api.keyring.getDecryptedSeed(keyring, pin);
|
2022-05-29 00:00:57 +02:00
|
|
|
|
2022-08-06 02:22:41 +02:00
|
|
|
String seedText;
|
|
|
|
if (seed == null) {
|
|
|
|
seedText = '';
|
2022-05-29 00:00:57 +02:00
|
|
|
} else {
|
2022-08-06 02:22:41 +02:00
|
|
|
seedText = seed.seed!.split('//')[0];
|
2022-05-29 00:00:57 +02:00
|
|
|
}
|
|
|
|
|
2022-08-06 02:22:41 +02:00
|
|
|
log.d(seedText);
|
|
|
|
return seedText;
|
2022-05-29 00:00:57 +02:00
|
|
|
}
|
|
|
|
|
2022-05-19 07:00:25 +02:00
|
|
|
int getDerivationNumber(String address) {
|
|
|
|
final account = getKeypair(address);
|
2022-05-20 15:15:29 +02:00
|
|
|
final deriveNbr = account.name!.split('//')[1];
|
2022-05-19 07:00:25 +02:00
|
|
|
return int.parse(deriveNbr);
|
|
|
|
}
|
|
|
|
|
2022-05-31 18:23:56 +02:00
|
|
|
Future<KeyPairData?> changePassword(BuildContext context, String address,
|
|
|
|
String passOld, String? passNew) async {
|
2022-05-19 07:00:25 +02:00
|
|
|
final account = getKeypair(address);
|
2022-08-06 02:22:41 +02:00
|
|
|
MyWalletsProvider myWalletProvider =
|
2022-05-31 18:23:56 +02:00
|
|
|
Provider.of<MyWalletsProvider>(context, listen: false);
|
2022-05-19 07:00:25 +02:00
|
|
|
keyring.setCurrent(account);
|
2022-08-06 02:22:41 +02:00
|
|
|
myWalletProvider.resetPinCode();
|
2022-05-19 07:00:25 +02:00
|
|
|
|
|
|
|
return await sdk.api.keyring.changePassword(keyring, passOld, passNew);
|
|
|
|
}
|
|
|
|
|
2022-02-19 23:51:12 +01:00
|
|
|
Future<void> deleteAllAccounts() async {
|
|
|
|
for (var account in keyring.allAccounts) {
|
|
|
|
await sdk.api.keyring.deleteAccount(keyring, account);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-24 16:51:40 +02:00
|
|
|
Future<void> deleteAccounts(List<String> address) async {
|
|
|
|
for (var a in address) {
|
|
|
|
final account = getKeypair(a);
|
|
|
|
await sdk.api.keyring.deleteAccount(keyring, account);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-19 07:00:25 +02:00
|
|
|
Future<String> generateMnemonic({String lang = appLang}) async {
|
2022-08-17 17:50:05 +02:00
|
|
|
final gen = await sdk.api.keyring
|
|
|
|
.generateMnemonic(currencyParameters['ss58'] ?? initSs58);
|
2022-02-18 19:49:37 +01:00
|
|
|
generatedMnemonic = gen.mnemonic!;
|
2022-02-19 23:51:12 +01:00
|
|
|
|
2022-02-22 10:39:45 +01:00
|
|
|
return gen.mnemonic!;
|
2022-02-18 18:57:03 +01:00
|
|
|
}
|
2022-02-19 23:51:12 +01:00
|
|
|
|
2022-08-06 02:22:41 +02:00
|
|
|
Future<String> setCurrentWallet(WalletData wallet) async {
|
2022-05-28 21:15:47 +02:00
|
|
|
final currentChestNumber = configBox.get('currentChest');
|
2022-08-06 02:22:41 +02:00
|
|
|
ChestData newChestData = chestBox.get(currentChestNumber)!;
|
|
|
|
newChestData.defaultWallet = wallet.number;
|
|
|
|
await chestBox.put(currentChestNumber, newChestData);
|
2022-05-28 21:15:47 +02:00
|
|
|
|
2022-05-19 13:44:22 +02:00
|
|
|
try {
|
2022-08-06 02:22:41 +02:00
|
|
|
final acc = getKeypair(wallet.address!);
|
2022-05-19 13:44:22 +02:00
|
|
|
keyring.setCurrent(acc);
|
|
|
|
return acc.address!;
|
|
|
|
} catch (e) {
|
|
|
|
return (e.toString());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
KeyPairData getCurrentWallet() {
|
|
|
|
try {
|
|
|
|
final acc = keyring.current;
|
|
|
|
return acc;
|
|
|
|
} catch (e) {
|
|
|
|
return KeyPairData();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-11 15:47:08 +02:00
|
|
|
Future<String> derive(
|
|
|
|
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('//');
|
|
|
|
generatedMnemonic = seedList[0];
|
|
|
|
|
|
|
|
return await importAccount(
|
|
|
|
mnemonic: generatedMnemonic,
|
|
|
|
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];
|
|
|
|
|
2022-08-15 17:41:12 +02:00
|
|
|
return await importAccount(password: password);
|
2022-08-11 15:47:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2022-08-18 01:31:47 +02:00
|
|
|
Future csToV2Address(String salt, String password) async {
|
2022-08-16 16:13:11 +02:00
|
|
|
final scrypt = pc.KeyDerivator('scrypt');
|
|
|
|
|
|
|
|
scrypt.init(
|
|
|
|
pc.ScryptParameters(
|
|
|
|
4096,
|
|
|
|
16,
|
|
|
|
1,
|
|
|
|
32,
|
|
|
|
Uint8List.fromList(salt.codeUnits),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
final rawSeed = scrypt.process(Uint8List.fromList(password.codeUnits));
|
|
|
|
final rawSeedHex = '0x${HEX.encode(rawSeed)}';
|
|
|
|
|
|
|
|
// Just get the address without keystore
|
2022-08-18 01:31:47 +02:00
|
|
|
final newAddress = await sdk.api.keyring.addressFromRawSeed(
|
|
|
|
currencyParameters['ss58']!,
|
|
|
|
cryptoType: CryptoType.ed25519,
|
|
|
|
rawSeed: rawSeedHex);
|
|
|
|
|
|
|
|
// final json = await sdk.api.keyring.importAccount(keyring,
|
|
|
|
// keyType: KeyType.rawSeed,
|
|
|
|
// key: rawSeedHex,
|
|
|
|
// name: 'test',
|
|
|
|
// password: 'password',
|
|
|
|
// derivePath: '',
|
|
|
|
// cryptoType: CryptoType.ed25519);
|
|
|
|
|
|
|
|
// final keypair = await sdk.api.keyring.addAccount(
|
|
|
|
// keyring,
|
|
|
|
// keyType: KeyType.rawSeed,
|
|
|
|
// acc: json!,
|
|
|
|
// password: password,
|
|
|
|
// );
|
|
|
|
// await sdk.api.keyring.deleteAccount(keyring, keypair);
|
2022-08-16 16:13:11 +02:00
|
|
|
|
|
|
|
// final keypair2 = KeyPairData.fromJson(json as Map<String, dynamic>);
|
|
|
|
|
2022-08-18 01:31:47 +02:00
|
|
|
// g1V1NewAddress = keypair.address!;
|
|
|
|
g1V1NewAddress = newAddress.address!;
|
|
|
|
notifyListeners();
|
2022-08-16 16:13:11 +02:00
|
|
|
}
|
|
|
|
|
2022-08-18 14:26:12 +02:00
|
|
|
Future<List> getBalanceAndIdtyStatus(String address, String myAddress) async {
|
|
|
|
final balance =
|
|
|
|
address == '' ? {'transferableBalance': 0} : await getBalance(address);
|
|
|
|
final thisIdtyStatus = address == '' ? 'noid' : await idtyStatus(address);
|
|
|
|
final thisHasConsumer =
|
|
|
|
address == '' ? false : await hasAccountConsumers(address);
|
|
|
|
final myIdtyStatus = await idtyStatus(myAddress);
|
|
|
|
|
|
|
|
log.d('tatata: $myIdtyStatus');
|
|
|
|
|
|
|
|
return [
|
|
|
|
balance['transferableBalance'],
|
|
|
|
thisIdtyStatus,
|
|
|
|
myIdtyStatus,
|
|
|
|
thisHasConsumer
|
|
|
|
];
|
|
|
|
}
|
|
|
|
|
2022-08-11 15:47:08 +02:00
|
|
|
//////////////////////////////////////
|
|
|
|
///////// 5: CALLS EXECUTION /////////
|
|
|
|
//////////////////////////////////////
|
|
|
|
|
2022-05-27 08:54:29 +02:00
|
|
|
Future<String> pay(
|
2022-05-19 13:44:22 +02:00
|
|
|
{required String fromAddress,
|
|
|
|
required String destAddress,
|
|
|
|
required double amount,
|
|
|
|
required String password}) async {
|
2022-05-26 02:19:29 +02:00
|
|
|
transactionStatus = '';
|
2022-06-08 02:30:19 +02:00
|
|
|
final fromPubkey = await sdk.api.account.decodeAddress([fromAddress]);
|
2022-08-11 15:47:08 +02:00
|
|
|
final int amountUnit = (amount * 100).toInt();
|
|
|
|
|
2022-02-19 23:51:12 +01:00
|
|
|
final sender = TxSenderData(
|
2022-06-08 02:30:19 +02:00
|
|
|
fromAddress,
|
2022-08-11 15:47:08 +02:00
|
|
|
fromPubkey!.keys.first,
|
2022-02-19 23:51:12 +01:00
|
|
|
);
|
2022-08-11 15:47:08 +02:00
|
|
|
|
2022-08-11 19:19:50 +02:00
|
|
|
final globalBalance = await getBalance(fromAddress);
|
|
|
|
TxInfoData txInfo;
|
2022-08-12 10:04:55 +02:00
|
|
|
List txOptions = [];
|
|
|
|
String? rawParams;
|
2022-08-11 19:19:50 +02:00
|
|
|
|
|
|
|
if (globalBalance['unclaimedUds'] == 0) {
|
|
|
|
txInfo = TxInfoData('balances',
|
|
|
|
amount == -1 ? 'transferAll' : 'transferKeepAlive', sender);
|
|
|
|
txOptions = [destAddress, amount == -1 ? false : amountUnit];
|
|
|
|
} else {
|
|
|
|
txInfo = TxInfoData(
|
|
|
|
'utility',
|
|
|
|
'batchAll',
|
|
|
|
sender,
|
|
|
|
);
|
2022-08-12 10:04:55 +02:00
|
|
|
const tx1 = 'api.tx.universalDividend.claimUds()';
|
|
|
|
final tx2 = amount == -1
|
|
|
|
? 'api.tx.balances.transferAll(false)'
|
|
|
|
: 'api.tx.balances.transferKeepAlive("$destAddress", $amountUnit)';
|
2022-08-11 19:19:50 +02:00
|
|
|
|
2022-08-12 10:04:55 +02:00
|
|
|
rawParams = '[[$tx1, $tx2]]';
|
2022-08-11 19:19:50 +02:00
|
|
|
}
|
|
|
|
|
2022-08-13 13:45:43 +02:00
|
|
|
// log.d('pay args: ${txInfo.module}, ${txInfo.call}, $txOptions, $rawParams');
|
2022-08-12 10:04:55 +02:00
|
|
|
return await executeCall(txInfo, txOptions, password, rawParams);
|
2022-05-27 08:54:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
Future<String> certify(
|
|
|
|
String fromAddress, String password, String toAddress) async {
|
|
|
|
transactionStatus = '';
|
|
|
|
|
2022-08-06 02:22:41 +02:00
|
|
|
final myIdtyStatus = await idtyStatus(fromAddress);
|
|
|
|
final toIdtyStatus = await idtyStatus(toAddress);
|
2022-05-27 08:54:29 +02:00
|
|
|
|
2022-08-08 16:24:40 +02:00
|
|
|
final fromIndex = await getIdentityIndexOf(fromAddress);
|
|
|
|
final toIndex = await getIdentityIndexOf(toAddress);
|
|
|
|
|
2022-08-06 02:22:41 +02:00
|
|
|
if (myIdtyStatus != 'Validated') {
|
2022-05-27 08:54:29 +02:00
|
|
|
transactionStatus = 'notMember';
|
|
|
|
notifyListeners();
|
|
|
|
return 'notMember';
|
|
|
|
}
|
|
|
|
|
2022-08-11 15:47:08 +02:00
|
|
|
final sender = _setSender();
|
2022-05-27 08:54:29 +02:00
|
|
|
TxInfoData txInfo;
|
2022-08-10 07:18:04 +02:00
|
|
|
List txOptions = [];
|
2022-08-12 10:04:55 +02:00
|
|
|
String? rawParams;
|
2022-05-27 08:54:29 +02:00
|
|
|
|
2022-08-11 15:47:08 +02:00
|
|
|
final toCerts = await getCerts(toAddress);
|
2022-08-17 17:50:05 +02:00
|
|
|
|
|
|
|
// log.d('debug: ${currencyParameters['minCertForMembership']}');
|
2022-08-11 15:47:08 +02:00
|
|
|
|
2022-08-06 02:22:41 +02:00
|
|
|
if (toIdtyStatus == 'noid') {
|
2022-05-27 08:54:29 +02:00
|
|
|
txInfo = TxInfoData(
|
|
|
|
'identity',
|
|
|
|
'createIdentity',
|
|
|
|
sender,
|
|
|
|
);
|
2022-08-10 07:18:04 +02:00
|
|
|
txOptions = [toAddress];
|
2022-08-06 02:22:41 +02:00
|
|
|
} else if (toIdtyStatus == 'Validated' ||
|
|
|
|
toIdtyStatus == 'ConfirmedByOwner') {
|
2022-08-17 17:50:05 +02:00
|
|
|
if (toCerts[0] >= currencyParameters['minCertForMembership']! - 1 &&
|
2022-08-08 16:24:40 +02:00
|
|
|
toIdtyStatus != 'Validated') {
|
2022-08-08 18:56:59 +02:00
|
|
|
log.i('Batch cert and membership validation');
|
2022-08-12 10:04:55 +02:00
|
|
|
txInfo = TxInfoData(
|
|
|
|
'utility',
|
|
|
|
'batchAll',
|
|
|
|
sender,
|
|
|
|
);
|
2022-08-16 19:59:53 +02:00
|
|
|
final tx1 = 'api.tx.cert.addCert($fromIndex, $toIndex)';
|
|
|
|
final tx2 = 'api.tx.identity.validateIdentity($toIndex)';
|
2022-08-12 10:04:55 +02:00
|
|
|
|
|
|
|
rawParams = '[[$tx1, $tx2]]';
|
2022-08-08 16:24:40 +02:00
|
|
|
} else {
|
|
|
|
txInfo = TxInfoData(
|
|
|
|
'cert',
|
|
|
|
'addCert',
|
|
|
|
sender,
|
|
|
|
);
|
2022-08-10 07:18:04 +02:00
|
|
|
txOptions = [fromIndex, toIndex];
|
2022-08-08 16:24:40 +02:00
|
|
|
}
|
2022-05-27 08:54:29 +02:00
|
|
|
} else {
|
|
|
|
transactionStatus = 'cantBeCert';
|
|
|
|
notifyListeners();
|
|
|
|
return 'cantBeCert';
|
|
|
|
}
|
|
|
|
|
2022-08-06 02:22:41 +02:00
|
|
|
log.d('Cert action: ${txInfo.call!}');
|
2022-08-12 10:04:55 +02:00
|
|
|
return await executeCall(txInfo, txOptions, password, rawParams);
|
2022-02-19 23:51:12 +01:00
|
|
|
}
|
2022-02-20 20:49:05 +01:00
|
|
|
|
2022-08-12 10:04:55 +02:00
|
|
|
// Future claimUDs(String password) async {
|
|
|
|
// final sender = TxSenderData(
|
|
|
|
// keyring.current.address,
|
|
|
|
// keyring.current.pubKey,
|
|
|
|
// );
|
2022-05-24 16:51:40 +02:00
|
|
|
|
2022-08-12 10:04:55 +02:00
|
|
|
// final txInfo = TxInfoData(
|
|
|
|
// 'universalDividend',
|
|
|
|
// 'claimUds',
|
|
|
|
// sender,
|
|
|
|
// );
|
2022-08-08 18:56:59 +02:00
|
|
|
|
2022-08-12 10:04:55 +02:00
|
|
|
// return await executeCall(txInfo, [], password);
|
|
|
|
// }
|
2022-05-24 16:51:40 +02:00
|
|
|
|
|
|
|
Future<String> confirmIdentity(
|
2022-05-27 08:54:29 +02:00
|
|
|
String fromAddress, String name, String password) async {
|
2022-08-06 02:22:41 +02:00
|
|
|
log.d('me: ${keyring.current.address!}');
|
2022-05-24 16:51:40 +02:00
|
|
|
|
|
|
|
final sender = TxSenderData(
|
|
|
|
keyring.current.address,
|
|
|
|
keyring.current.pubKey,
|
|
|
|
);
|
|
|
|
|
|
|
|
final txInfo = TxInfoData(
|
|
|
|
'identity',
|
|
|
|
'confirmIdentity',
|
|
|
|
sender,
|
|
|
|
);
|
2022-08-11 15:47:08 +02:00
|
|
|
final txOptions = [name];
|
2022-05-24 16:51:40 +02:00
|
|
|
|
2022-08-11 15:47:08 +02:00
|
|
|
return await executeCall(txInfo, txOptions, password);
|
2022-06-04 17:24:33 +02:00
|
|
|
}
|
|
|
|
|
2022-08-18 18:22:36 +02:00
|
|
|
Future<String> migrateIdentity(
|
|
|
|
{required String fromAddress,
|
|
|
|
required String destAddress,
|
|
|
|
required String password}) async {
|
|
|
|
transactionStatus = '';
|
|
|
|
final fromPubkey = await sdk.api.account.decodeAddress([fromAddress]);
|
|
|
|
final sender = TxSenderData(
|
|
|
|
fromAddress,
|
|
|
|
fromPubkey!.keys.first,
|
|
|
|
);
|
|
|
|
|
|
|
|
// final globalBalance = await getBalance(fromAddress);
|
|
|
|
TxInfoData txInfo;
|
|
|
|
List txOptions = [];
|
|
|
|
String? rawParams;
|
|
|
|
// final destKeyring = getKeypair(destAddress);
|
|
|
|
|
|
|
|
final genesisHash = await getGenesisHash();
|
|
|
|
final idtyIndex = await getIdentityIndexOf(destAddress);
|
|
|
|
// final oldPubkey = await addressToPubkey(fromAddress);
|
|
|
|
final messageToSign = 'icok$genesisHash$idtyIndex$fromAddress';
|
|
|
|
final newKeySig = messageToSign;
|
|
|
|
|
|
|
|
txInfo = TxInfoData(
|
|
|
|
'utility',
|
|
|
|
'batchAll',
|
|
|
|
sender,
|
|
|
|
);
|
|
|
|
const tx1 = 'api.tx.universalDividend.claimUds()';
|
|
|
|
final tx2 = 'api.tx.identity.changeOwnerKey("$destAddress", "$newKeySig")';
|
|
|
|
const tx3 = 'api.tx.balances.transferAll(false)';
|
|
|
|
|
|
|
|
rawParams = '[[$tx1, $tx2, $tx3]]';
|
|
|
|
|
|
|
|
log.d(
|
|
|
|
'g1migration args: ${txInfo.module}, ${txInfo.call}, $txOptions, $rawParams');
|
|
|
|
return await executeCall(txInfo, txOptions, password, rawParams);
|
|
|
|
}
|
|
|
|
|
2022-06-05 02:22:22 +02:00
|
|
|
Future revokeIdentity(String address, String password) async {
|
2022-08-08 16:24:40 +02:00
|
|
|
final idtyIndex = await getIdentityIndexOf(address);
|
2022-06-05 02:22:22 +02:00
|
|
|
|
|
|
|
final sender = TxSenderData(
|
|
|
|
keyring.current.address,
|
|
|
|
keyring.current.pubKey,
|
|
|
|
);
|
|
|
|
|
|
|
|
log.d(sender.address);
|
|
|
|
TxInfoData txInfo;
|
|
|
|
|
|
|
|
txInfo = TxInfoData(
|
|
|
|
'membership',
|
|
|
|
'revokeMembership',
|
|
|
|
sender,
|
|
|
|
);
|
|
|
|
|
2022-08-11 15:47:08 +02:00
|
|
|
final txOptions = [idtyIndex];
|
2022-06-05 02:22:22 +02:00
|
|
|
|
2022-08-11 15:47:08 +02:00
|
|
|
return await executeCall(txInfo, txOptions, password);
|
2022-06-05 02:22:22 +02:00
|
|
|
}
|
|
|
|
|
2022-08-18 14:26:12 +02:00
|
|
|
Future migrateCsToV2(String salt, String password, String destAddress,
|
|
|
|
{required double balance, String idtyStatus = 'noid'}) async {
|
|
|
|
final scrypt = pc.KeyDerivator('scrypt');
|
|
|
|
|
|
|
|
scrypt.init(
|
|
|
|
pc.ScryptParameters(
|
|
|
|
4096,
|
|
|
|
16,
|
|
|
|
1,
|
|
|
|
32,
|
|
|
|
Uint8List.fromList(salt.codeUnits),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
final rawSeed = scrypt.process(Uint8List.fromList(password.codeUnits));
|
|
|
|
final rawSeedHex = '0x${HEX.encode(rawSeed)}';
|
|
|
|
|
|
|
|
final json = await sdk.api.keyring.importAccount(keyring,
|
|
|
|
keyType: KeyType.rawSeed,
|
|
|
|
key: rawSeedHex,
|
|
|
|
name: 'test',
|
|
|
|
password: 'password',
|
|
|
|
derivePath: '',
|
|
|
|
cryptoType: CryptoType.ed25519);
|
|
|
|
|
|
|
|
final keypair = await sdk.api.keyring.addAccount(
|
|
|
|
keyring,
|
|
|
|
keyType: KeyType.rawSeed,
|
|
|
|
acc: json!,
|
|
|
|
password: password,
|
|
|
|
);
|
|
|
|
|
2022-08-18 18:22:36 +02:00
|
|
|
log.d('g1migration idtyStatus: $idtyStatus');
|
|
|
|
if (idtyStatus != 'noid') {
|
|
|
|
await migrateIdentity(
|
|
|
|
fromAddress: keypair.address!,
|
|
|
|
destAddress: destAddress,
|
|
|
|
password: 'password');
|
|
|
|
} else if (balance != 0) {
|
2022-08-18 14:26:12 +02:00
|
|
|
await pay(
|
|
|
|
fromAddress: keypair.address!,
|
|
|
|
destAddress: destAddress,
|
|
|
|
amount: -1,
|
|
|
|
password: 'password');
|
|
|
|
}
|
|
|
|
|
|
|
|
await sdk.api.keyring.deleteAccount(keyring, keypair);
|
2022-08-18 01:31:47 +02:00
|
|
|
}
|
|
|
|
|
2022-08-11 15:47:08 +02:00
|
|
|
void reload() {
|
|
|
|
notifyListeners();
|
2022-08-08 18:56:59 +02:00
|
|
|
}
|
2022-02-19 23:51:12 +01:00
|
|
|
}
|
|
|
|
|
2022-08-11 15:47:08 +02:00
|
|
|
////////////////////////////////////////////
|
|
|
|
/////// 6: UI ELEMENTS (off class) /////////
|
|
|
|
////////////////////////////////////////////
|
|
|
|
|
2022-02-19 23:51:12 +01:00
|
|
|
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});
|
|
|
|
}
|
|
|
|
|
2022-05-20 15:15:29 +02:00
|
|
|
void snackNode(BuildContext context, bool isConnected) {
|
2022-08-06 02:22:41 +02:00
|
|
|
String message;
|
2022-05-20 15:15:29 +02:00
|
|
|
if (!isConnected) {
|
2022-08-17 23:28:19 +02:00
|
|
|
message = "noDuniterNodeAvailableTryLater".tr();
|
2022-05-20 15:15:29 +02:00
|
|
|
} else {
|
2022-08-06 02:22:41 +02:00
|
|
|
SubstrateSdk sub = Provider.of<SubstrateSdk>(context, listen: false);
|
2022-05-27 06:11:09 +02:00
|
|
|
|
2022-08-06 02:22:41 +02:00
|
|
|
message =
|
|
|
|
"${"youAreConnectedToNode".tr()}\n${sub.getConnectedEndpoint()!.split('//')[1]}";
|
2022-05-20 15:15:29 +02:00
|
|
|
}
|
2022-05-21 06:47:26 +02:00
|
|
|
final snackBar = SnackBar(
|
|
|
|
padding: const EdgeInsets.all(20),
|
2022-08-06 02:22:41 +02:00
|
|
|
content: Text(message, style: const TextStyle(fontSize: 16)),
|
2022-05-27 12:54:25 +02:00
|
|
|
duration: const Duration(seconds: 4));
|
2022-05-20 15:15:29 +02:00
|
|
|
ScaffoldMessenger.of(context).showSnackBar(snackBar);
|
|
|
|
}
|
|
|
|
|
2022-02-19 23:51:12 +01:00
|
|
|
String getShortPubkey(String pubkey) {
|
2022-05-28 00:17:50 +02:00
|
|
|
String pubkeyShort = truncate(pubkey, 7,
|
2022-02-19 23:51:12 +01:00
|
|
|
omission: String.fromCharCode(0x2026),
|
|
|
|
position: TruncatePosition.end) +
|
2022-05-28 00:17:50 +02:00
|
|
|
truncate(pubkey, 6, omission: "", position: TruncatePosition.start);
|
2022-02-19 23:51:12 +01:00
|
|
|
return pubkeyShort;
|
2022-02-18 00:20:21 +01:00
|
|
|
}
|
2022-05-24 16:51:40 +02:00
|
|
|
|
|
|
|
class PasswordException implements Exception {
|
|
|
|
String cause;
|
|
|
|
PasswordException(this.cause);
|
|
|
|
}
|