gecko/lib/providers/substrate_sdk.dart

812 lines
23 KiB
Dart
Raw Normal View History

2022-06-17 01:13:14 +02:00
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
2022-02-18 18:57:03 +01:00
import 'package:flutter/services.dart';
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';
import 'package:gecko/providers/home.dart';
import 'package:gecko/providers/my_wallets.dart';
2022-02-18 02:19:08 +01:00
import 'package:polkawallet_sdk/api/apiKeyring.dart';
import 'package:polkawallet_sdk/api/types/networkParams.dart';
2022-02-19 23:51:12 +01:00
import 'package:polkawallet_sdk/api/types/txInfoData.dart';
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';
import 'package:provider/provider.dart';
2022-02-19 23:51:12 +01:00
import 'package:truncate/truncate.dart';
// import 'package:web_socket_channel/io.dart';
class SubstrateSdk with ChangeNotifier {
final WalletSDK sdk = WalletSDK();
final Keyring keyring = Keyring();
2022-02-18 19:49:37 +01:00
String generatedMnemonic = '';
bool sdkReady = false;
2022-02-18 18:57:03 +01:00
bool sdkLoading = false;
bool nodeConnected = false;
2022-02-18 19:49:37 +01:00
bool importIsLoading = false;
int blocNumber = 0;
bool isLoadingEndpoint = false;
2022-05-25 20:40:55 +02:00
String debugConnection = '';
2022-05-26 02:19:29 +02:00
String transactionStatus = '';
int ss58 = 42;
2022-02-18 02:19:08 +01:00
TextEditingController jsonKeystore = TextEditingController();
TextEditingController keystorePassword = TextEditingController();
/////////////////////////////////////
////////// 1: API METHODS ///////////
/////////////////////////////////////
2022-08-12 10:04:55 +02:00
Future<String> executeCall(TxInfoData txInfo, txOptions, String password,
[String? rawParams]) async {
try {
final hash = await sdk.api.tx
2022-08-12 10:04:55 +02:00
.signAndSend(txInfo, txOptions, password, rawParam: rawParams)
.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();
}
}
Future getStorage(String call) async {
return await sdk.webView!.evalJavascript('api.query.$call');
}
TxSenderData _setSender() {
return TxSenderData(
keyring.current.address,
keyring.current.pubKey,
);
}
////////////////////////////////////////////
////////// 2: GET ONCHAIN STORAGE //////////
////////////////////////////////////////////
Future<List<AddressInfo>> getKeyStoreAddress() async {
List<AddressInfo> result = [];
for (var element in keyring.allAccounts) {
final account = AddressInfo(address: element.address);
final globalBalance = await getBalance(element.address!);
account.balance = globalBalance['transferableBalance']!;
result.add(account);
}
return result;
}
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<Map<String, dynamic>> getParameters() async {
final currencyParameters =
await getStorage('parameters.parametersStorage()') ?? {};
return currencyParameters;
}
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;
// }
Future<Map<String, double>> getBalance(String address) async {
// 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
final int unclaimedUds = _computeUnclaimUds(currentUdIndex,
idtyData?['data']?['firstEligibleUd'] ?? 0, pastReevals);
// Calculate transferable and potential balance
final int transferableBalance =
(balanceGlobal['data']['free'] + unclaimedUds);
Map<String, double> finalBalances = {
'transferableBalance': transferableBalance / 100,
'free': balanceGlobal['data']['free'] / 100,
'unclaimedUds': unclaimedUds / 100,
'reserved': balanceGlobal['data']['reserved'] / 100,
};
// log.i(finalBalances);
return finalBalances;
}
int _computeUnclaimUds(
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<int> getSs58Prefix() async {
final List res = await sdk.webView!.evalJavascript(
'api.consts.system.ss58Prefix.words',
wrapPromise: false) ??
[42];
ss58 = res[0];
log.d(ss58);
return ss58;
}
Future<bool> isMemberGet(String address) async {
return await idtyStatus(address) == 'Validated';
}
Future<String> getMemberAddress() async {
// 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 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);
}
log.d('tatatatata: ${nextIssuableOn - blocNumber}');
}
return result;
}
Future<Map> getCertMeta(String address) async {
var idtyIndex = await getIdentityIndexOf(address);
final certMeta =
await getStorage('cert.storageIdtyCertMeta($idtyIndex)') ?? '';
return certMeta;
}
Future<String> idtyStatus(String address, [bool smooth = true]) async {
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';
}
}
Future getCurencyName() async {}
/////////////////////////////////////
////// 3: SUBSTRATE CONNECTION //////
/////////////////////////////////////
Future<void> initApi() async {
2022-02-18 18:57:03 +01:00
sdkLoading = true;
2022-02-19 23:51:12 +01:00
await keyring.init([ss58]);
keyring.setSS58(ss58);
await sdk.init(keyring);
sdkReady = true;
2022-02-18 18:57:03 +01:00
sdkLoading = false;
notifyListeners();
}
String? getConnectedEndpoint() {
return sdk.api.connectedNode?.endpoint;
}
Future<void> connectNode(BuildContext ctx) async {
HomeProvider homeProvider = Provider.of<HomeProvider>(ctx, listen: false);
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;
if (sdk.api.connectedNode?.endpoint != null) {
await sdk.api.setting.unsubscribeBestNumber();
2022-02-19 23:51:12 +01:00
}
isLoadingEndpoint = true;
notifyListeners();
2022-07-14 18:18:19 +02:00
final res = await sdk.api.connectNode(keyring, listEndpoints).timeout(
Duration(milliseconds: timeout),
2022-02-19 23:51:12 +01:00
onTimeout: () => null,
);
isLoadingEndpoint = false;
notifyListeners();
if (res != null) {
nodeConnected = true;
// await getSs58Prefix();
2022-05-25 20:40:55 +02:00
// Subscribe bloc number
sdk.api.setting.subscribeBestNumber((res) {
blocNumber = int.parse(res.toString());
// log.d(sdk.api.connectedNode?.endpoint);
2022-06-01 21:00:17 +02:00
if (sdk.api.connectedNode?.endpoint == null) {
nodeConnected = false;
homeProvider.changeMessage("networkLost".tr(), 0);
} else {
nodeConnected = true;
2022-06-01 21:00:17 +02:00
}
2022-05-25 20:40:55 +02:00
notifyListeners();
});
2022-06-04 23:32:44 +02:00
// currencyName = await getCurencyName();
notifyListeners();
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);
// snackNode(ctx, true);
} else {
nodeConnected = false;
2022-05-25 20:40:55 +02:00
debugConnection = res.toString();
notifyListeners();
homeProvider.changeMessage("noDuniterEndointAvailable".tr(), 0);
// snackNode(ctx, false);
}
log.d(sdk.api.connectedNode?.endpoint);
}
2022-02-18 02:19:08 +01:00
2022-07-14 18:18:19 +02:00
List<NetworkParams> getDuniterBootstrap() {
List<NetworkParams> node = [];
for (String endpoint in configBox.get('endpoint')) {
2022-07-14 18:18:19 +02:00
final n = NetworkParams();
n.name = currencyName;
n.endpoint = endpoint;
2022-07-14 18:18:19 +02:00
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;
}
2022-05-04 19:00:09 +02:00
Future<String> importAccount(
{String mnemonic = '',
bool fromMnemonic = false,
String derivePath = '',
String password = ''}) async {
2022-02-22 10:39:45 +01:00
// toy exercise immense month enter answer table prefer speed cycle gold phone
final clipboardData = await Clipboard.getData(Clipboard.kTextPlain);
2022-05-04 19:00:09 +02:00
if (mnemonic != '') {
fromMnemonic = true;
generatedMnemonic = mnemonic;
} else if (clipboardData!.text!.split(' ').length == 12) {
2022-02-22 10:39:45 +01:00
fromMnemonic = true;
generatedMnemonic = clipboardData.text!;
}
if (password == '') {
password = keystorePassword.text;
}
2022-02-19 23:51:12 +01:00
final KeyType keytype;
final String keyToImport;
if (fromMnemonic) {
keytype = KeyType.mnemonic;
keyToImport = generatedMnemonic;
} else {
keytype = KeyType.keystore;
keyToImport = jsonKeystore.text.replaceAll("'", "\\'");
}
2022-02-18 19:49:37 +01:00
importIsLoading = true;
notifyListeners();
2022-05-04 19:00:09 +02:00
if (clipboardData?.text != null) jsonKeystore.text = clipboardData!.text!;
var json = await sdk.api.keyring
.importAccount(keyring,
keyType: keytype,
key: keyToImport,
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 '';
// log.d(json);
2022-02-19 23:51:12 +01:00
try {
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,
password: password,
2022-02-19 23:51:12 +01:00
);
} catch (e) {
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();
return keyring.allAccounts.last.address!;
2022-02-18 02:19:08 +01:00
}
//////////////////////////////////
/////// 4: CRYPTOGRAPHY //////////
//////////////////////////////////
2022-05-28 21:15:47 +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);
return await sdk.api.keyring.checkPassword(account, pass);
}
Future<String> getSeed(String address, String pin) async {
2022-05-29 00:00:57 +02:00
final account = getKeypair(address);
keyring.setCurrent(account);
final seed = await sdk.api.keyring.getDecryptedSeed(keyring, pin);
2022-05-29 00:00:57 +02:00
String seedText;
if (seed == null) {
seedText = '';
2022-05-29 00:00:57 +02:00
} else {
seedText = seed.seed!.split('//')[0];
2022-05-29 00:00:57 +02:00
}
log.d(seedText);
return seedText;
2022-05-29 00:00:57 +02:00
}
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);
keyring.setCurrent(account);
myWalletProvider.resetPinCode();
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);
}
}
Future<String> generateMnemonic({String lang = appLang}) async {
2022-02-19 23:51:12 +01:00
final gen = await sdk.api.keyring.generateMnemonic(ss58);
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
Future<String> setCurrentWallet(WalletData wallet) async {
2022-05-28 21:15:47 +02:00
final currentChestNumber = configBox.get('currentChest');
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 {
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();
}
}
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,
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);
}
//////////////////////////////////////
///////// 5: CALLS EXECUTION /////////
//////////////////////////////////////
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 = '';
final fromPubkey = await sdk.api.account.decodeAddress([fromAddress]);
final int amountUnit = (amount * 100).toInt();
2022-02-19 23:51:12 +01:00
final sender = TxSenderData(
fromAddress,
fromPubkey!.keys.first,
2022-02-19 23:51:12 +01:00
);
final globalBalance = await getBalance(fromAddress);
TxInfoData txInfo;
2022-08-12 10:04:55 +02:00
List txOptions = [];
String? rawParams;
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-12 10:04:55 +02:00
rawParams = '[[$tx1, $tx2]]';
}
// 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);
}
Future<String> certify(
String fromAddress, String password, String toAddress) async {
transactionStatus = '';
final myIdtyStatus = await idtyStatus(fromAddress);
final toIdtyStatus = await idtyStatus(toAddress);
final fromIndex = await getIdentityIndexOf(fromAddress);
final toIndex = await getIdentityIndexOf(toAddress);
if (myIdtyStatus != 'Validated') {
transactionStatus = 'notMember';
notifyListeners();
return 'notMember';
}
final sender = _setSender();
TxInfoData txInfo;
2022-08-10 07:18:04 +02:00
List txOptions = [];
2022-08-12 10:04:55 +02:00
String? rawParams;
final toCerts = await getCerts(toAddress);
final currencyParameters = await getParameters();
if (toIdtyStatus == 'noid') {
txInfo = TxInfoData(
'identity',
'createIdentity',
sender,
);
2022-08-10 07:18:04 +02:00
txOptions = [toAddress];
} else if (toIdtyStatus == 'Validated' ||
toIdtyStatus == 'ConfirmedByOwner') {
if (toCerts[0] >= currencyParameters['wotMinCertForMembership'] &&
toIdtyStatus != 'Validated') {
log.i('Batch cert and membership validation');
2022-08-12 10:04:55 +02:00
txInfo = TxInfoData(
'utility',
'batchAll',
sender,
);
final tx1 = 'cert.addCert($fromIndex, $toIndex)';
final tx2 = 'identity.validateIdentity($toIndex)';
rawParams = '[[$tx1, $tx2]]';
} else {
txInfo = TxInfoData(
'cert',
'addCert',
sender,
);
2022-08-10 07:18:04 +02:00
txOptions = [fromIndex, toIndex];
}
} else {
transactionStatus = 'cantBeCert';
notifyListeners();
return 'cantBeCert';
}
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-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-12 10:04:55 +02:00
// return await executeCall(txInfo, [], password);
// }
2022-05-24 16:51:40 +02:00
Future<String> confirmIdentity(
String fromAddress, String name, String password) async {
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,
);
final txOptions = [name];
2022-05-24 16:51:40 +02:00
return await executeCall(txInfo, txOptions, password);
}
2022-06-05 02:22:22 +02:00
Future revokeIdentity(String address, String password) async {
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,
);
final txOptions = [idtyIndex];
2022-06-05 02:22:22 +02:00
return await executeCall(txInfo, txOptions, password);
2022-06-05 02:22:22 +02:00
}
void reload() {
notifyListeners();
}
2022-02-19 23:51:12 +01: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});
}
void snackNode(BuildContext context, bool isConnected) {
String message;
if (!isConnected) {
message =
"${"noDuniterNodeAvailableTryLater".tr()}:\n${configBox.get('endpoint').first}";
} else {
SubstrateSdk sub = Provider.of<SubstrateSdk>(context, listen: false);
message =
"${"youAreConnectedToNode".tr()}\n${sub.getConnectedEndpoint()!.split('//')[1]}";
}
2022-05-21 06:47:26 +02:00
final snackBar = SnackBar(
padding: const EdgeInsets.all(20),
content: Text(message, style: const TextStyle(fontSize: 16)),
2022-05-27 12:54:25 +02:00
duration: const Duration(seconds: 4));
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-05-24 16:51:40 +02:00
class PasswordException implements Exception {
String cause;
PasswordException(this.cause);
}