Skip to content
Snippets Groups Projects
Commit b43a5fcd authored by poka's avatar poka
Browse files

feat: check indexer and node sync

parent 0e38099f
Branches
Tags
No related merge requests found
Pipeline #38189 waiting for manual action
const String getNameByAddressQ = r''' const getNameByAddressQ = r'''
query ($address: String!) { query ($address: String!) {
identityConnection( identityConnection(
where: { accountId: { _eq: $address } } where: { accountId: { _eq: $address } }
...@@ -14,7 +14,7 @@ query ($address: String!) { ...@@ -14,7 +14,7 @@ query ($address: String!) {
} }
'''; ''';
const String searchAddressByNameQ = r''' const searchAddressByNameQ = r'''
query ($name: String!) { query ($name: String!) {
identityConnection( identityConnection(
where: { name: { _ilike: $name } } where: { name: { _ilike: $name } }
...@@ -30,7 +30,7 @@ query ($name: String!) { ...@@ -30,7 +30,7 @@ query ($name: String!) {
} }
'''; ''';
const String getHistoryByAddressRelayQ = r''' const getHistoryByAddressRelayQ = r'''
query ($address: String!, $first: Int!, $after: String) { query ($address: String!, $first: Int!, $after: String) {
transferConnection( transferConnection(
after: $after after: $after
...@@ -64,7 +64,7 @@ query ($address: String!, $first: Int!, $after: String) { ...@@ -64,7 +64,7 @@ query ($address: String!, $first: Int!, $after: String) {
} }
'''; ''';
const String getCertsReceived = r''' const getCertsReceived = r'''
query ($address: String!) { query ($address: String!) {
certConnection( certConnection(
where: {receiver: {accountId: {_eq: $address}}} where: {receiver: {accountId: {_eq: $address}}}
...@@ -88,7 +88,7 @@ query ($address: String!) { ...@@ -88,7 +88,7 @@ query ($address: String!) {
} }
'''; ''';
const String getCertsSent = r''' const getCertsSent = r'''
query ($address: String!) { query ($address: String!) {
certConnection( certConnection(
where: {issuer: {accountId: {_eq: $address}}} where: {issuer: {accountId: {_eq: $address}}}
...@@ -112,7 +112,7 @@ query ($address: String!) { ...@@ -112,7 +112,7 @@ query ($address: String!) {
} }
'''; ''';
const String isIdtyExistQ = r''' const isIdtyExistQ = r'''
query ($name: String!) { query ($name: String!) {
identityConnection(where: {name: {_eq: ""}}) { identityConnection(where: {name: {_eq: ""}}) {
edges { edges {
...@@ -124,7 +124,7 @@ query ($name: String!) { ...@@ -124,7 +124,7 @@ query ($name: String!) {
} }
'''; ''';
const String getBlockchainStartQ = r''' const getBlockchainStartQ = r'''
query { query {
blockConnection(first: 1) { blockConnection(first: 1) {
edges { edges {
...@@ -137,7 +137,7 @@ query { ...@@ -137,7 +137,7 @@ query {
} }
'''; ''';
const String subscribeHistoryIssuedQ = r''' const subscribeHistoryIssuedQ = r'''
subscription ($address: String!) { subscription ($address: String!) {
accountConnection( accountConnection(
where: {id: {_eq: $address}} where: {id: {_eq: $address}}
...@@ -155,3 +155,11 @@ subscription ($address: String!) { ...@@ -155,3 +155,11 @@ subscription ($address: String!) {
} }
} }
'''; ''';
const getBlockByHash = r'''
query ($hash: bytea!) {
block(where: {hash: {_eq: $hash}}) {
height
}
}
''';
...@@ -8,6 +8,7 @@ import 'package:gecko/globals.dart'; ...@@ -8,6 +8,7 @@ import 'package:gecko/globals.dart';
import 'package:gecko/models/queries_indexer.dart'; import 'package:gecko/models/queries_indexer.dart';
import 'package:gecko/providers/substrate_sdk.dart'; import 'package:gecko/providers/substrate_sdk.dart';
import 'package:graphql_flutter/graphql_flutter.dart'; import 'package:graphql_flutter/graphql_flutter.dart';
import 'package:provider/provider.dart';
class DuniterIndexer with ChangeNotifier { class DuniterIndexer with ChangeNotifier {
Map<String, String?> walletNameIndexer = {}; Map<String, String?> walletNameIndexer = {};
...@@ -29,8 +30,7 @@ class DuniterIndexer with ChangeNotifier { ...@@ -29,8 +30,7 @@ class DuniterIndexer with ChangeNotifier {
final client = HttpClient(); final client = HttpClient();
client.connectionTimeout = const Duration(milliseconds: 4000); client.connectionTimeout = const Duration(milliseconds: 4000);
try { try {
final request = final request = await client.postUrl(Uri.parse('https://$endpoint/v1beta1/relay'));
await client.postUrl(Uri.parse('https://$endpoint/v1beta1/relay'));
final response = await request.close(); final response = await request.close();
if (response.statusCode != 200) { if (response.statusCode != 200) {
log.w('Indexer $endpoint is offline'); log.w('Indexer $endpoint is offline');
...@@ -39,6 +39,12 @@ class DuniterIndexer with ChangeNotifier { ...@@ -39,6 +39,12 @@ class DuniterIndexer with ChangeNotifier {
notifyListeners(); notifyListeners();
return false; return false;
} else { } else {
final isSynced = await isIndexerSynced('https://$endpoint/v1/graphql');
if (!isSynced) {
log.e('This endpoint is not synced, next');
return false;
}
indexerEndpoint = endpoint; indexerEndpoint = endpoint;
await configBox.put('indexerEndpoint', endpoint); await configBox.put('indexerEndpoint', endpoint);
// await configBox.put('customEndpoint', endpoint); // await configBox.put('customEndpoint', endpoint);
...@@ -60,9 +66,7 @@ class DuniterIndexer with ChangeNotifier { ...@@ -60,9 +66,7 @@ class DuniterIndexer with ChangeNotifier {
Future<String> getValidIndexerEndpoint() async { Future<String> getValidIndexerEndpoint() async {
// await configBox.delete('indexerEndpoint'); // await configBox.delete('indexerEndpoint');
listIndexerEndpoints = await rootBundle listIndexerEndpoints = await rootBundle.loadString('config/indexer_endpoints.json').then((jsonStr) => jsonDecode(jsonStr));
.loadString('config/indexer_endpoints.json')
.then((jsonStr) => jsonDecode(jsonStr));
// _listEndpoints.shuffle(); // _listEndpoints.shuffle();
listIndexerEndpoints.add('Personnalisé'); listIndexerEndpoints.add('Personnalisé');
...@@ -71,8 +75,7 @@ class DuniterIndexer with ChangeNotifier { ...@@ -71,8 +75,7 @@ class DuniterIndexer with ChangeNotifier {
return configBox.get('customIndexer'); return configBox.get('customIndexer');
} }
if (configBox.containsKey('indexerEndpoint') && if (configBox.containsKey('indexerEndpoint') && listIndexerEndpoints.contains(configBox.get('indexerEndpoint'))) {
listIndexerEndpoints.contains(configBox.get('indexerEndpoint'))) {
if (await checkIndexerEndpoint(configBox.get('indexerEndpoint'))) { if (await checkIndexerEndpoint(configBox.get('indexerEndpoint'))) {
return configBox.get('indexerEndpoint'); return configBox.get('indexerEndpoint');
} }
...@@ -104,6 +107,15 @@ class DuniterIndexer with ChangeNotifier { ...@@ -104,6 +107,15 @@ class DuniterIndexer with ChangeNotifier {
final request = await client.postUrl(Uri.parse(endpointPath)); final request = await client.postUrl(Uri.parse(endpointPath));
final response = await request.close(); final response = await request.close();
final isSynced = await isIndexerSynced('https://${listIndexerEndpoints[i]}/v1/graphql');
if (!isSynced) {
log.e('This endpoint is not synced, next');
statusCode = 40;
i++;
continue;
}
indexerEndpoint = listIndexerEndpoints[i]; indexerEndpoint = listIndexerEndpoints[i];
await configBox.put('indexerEndpoint', listIndexerEndpoints[i]); await configBox.put('indexerEndpoint', listIndexerEndpoints[i]);
...@@ -131,6 +143,37 @@ class DuniterIndexer with ChangeNotifier { ...@@ -131,6 +143,37 @@ class DuniterIndexer with ChangeNotifier {
return indexerEndpoint; return indexerEndpoint;
} }
Future<bool> isIndexerSynced(String endpoint) async {
try {
final sub = Provider.of<SubstrateSdk>(homeContext, listen: false);
var duniterFinilizedHash = await sub.getLastFinilizedHash();
final duniterFinilizedNumber = await sub.getBlockNumberByHash(duniterFinilizedHash);
duniterFinilizedHash = "\\x${duniterFinilizedHash.substring(2)}";
final indexerLink = HttpLink(endpoint);
final iClient = GraphQLClient(
cache: GraphQLCache(),
link: indexerLink,
);
final result = await iClient.query(QueryOptions(document: gql(getBlockByHash), variables: <String, dynamic>{'hash': duniterFinilizedHash}));
if (result.hasException || result.data == null || result.data!['block'].isEmpty) {
log.e('Indexer is not synced: ${result.exception} -- ${result.data}');
return false;
}
final indexerFinilizedNumber = result.data!['block'][0]['height'] as int;
if (duniterFinilizedNumber != indexerFinilizedNumber) {
log.e('Indexer is not synced');
return false;
}
return true;
} catch (e) {
log.e('An error occured while checking indexer sync: $e');
return false;
}
}
List parseHistory(List blockchainTX, String address) { List parseHistory(List blockchainTX, String address) {
List transBC = []; List transBC = [];
int i = 0; int i = 0;
...@@ -159,10 +202,8 @@ class DuniterIndexer with ChangeNotifier { ...@@ -159,10 +202,8 @@ class DuniterIndexer with ChangeNotifier {
return transBC; return transBC;
} }
FetchMoreOptions? mergeQueryResult(QueryResult result, FetchMoreOptions? opts, FetchMoreOptions? mergeQueryResult(QueryResult result, FetchMoreOptions? opts, String address, int nRepositories) {
String address, int nRepositories) { final List<dynamic> blockchainTX = (result.data!['transferConnection']['edges'] as List<dynamic>);
final List<dynamic> blockchainTX =
(result.data!['transferConnection']['edges'] as List<dynamic>);
pageInfo = result.data!['transferConnection']['pageInfo']; pageInfo = result.data!['transferConnection']['pageInfo'];
fetchMoreCursor = pageInfo!['endCursor']; fetchMoreCursor = pageInfo!['endCursor'];
...@@ -174,10 +215,8 @@ class DuniterIndexer with ChangeNotifier { ...@@ -174,10 +215,8 @@ class DuniterIndexer with ChangeNotifier {
variables: {'after': fetchMoreCursor, 'first': nRepositories}, variables: {'after': fetchMoreCursor, 'first': nRepositories},
updateQuery: (previousResultData, fetchMoreResultData) { updateQuery: (previousResultData, fetchMoreResultData) {
final List<dynamic> repos = [ final List<dynamic> repos = [
...previousResultData!['transferConnection']['edges'] ...previousResultData!['transferConnection']['edges'] as List<dynamic>,
as List<dynamic>, ...fetchMoreResultData!['transferConnection']['edges'] as List<dynamic>
...fetchMoreResultData!['transferConnection']['edges']
as List<dynamic>
]; ];
fetchMoreResultData['transferConnection']['edges'] = repos; fetchMoreResultData['transferConnection']['edges'] = repos;
...@@ -213,16 +252,14 @@ class DuniterIndexer with ChangeNotifier { ...@@ -213,16 +252,14 @@ class DuniterIndexer with ChangeNotifier {
Future<DateTime> getBlockStart() async { Future<DateTime> getBlockStart() async {
final result = await _execQuery(getBlockchainStartQ, {}); final result = await _execQuery(getBlockchainStartQ, {});
if (!result.hasException) { if (!result.hasException) {
startBlockchainTime = DateTime.parse( startBlockchainTime = DateTime.parse(result.data!['blockConnection']['edges'][0]['node']['timestamp']);
result.data!['blockConnection']['edges'][0]['node']['timestamp']);
startBlockchainInitialized = true; startBlockchainInitialized = true;
return startBlockchainTime; return startBlockchainTime;
} }
return DateTime(0, 0, 0, 0, 0); return DateTime(0, 0, 0, 0, 0);
} }
Future<QueryResult> _execQuery( Future<QueryResult> _execQuery(String query, Map<String, dynamic> variables) async {
String query, Map<String, dynamic> variables) async {
final options = QueryOptions(document: gql(query), variables: variables); final options = QueryOptions(document: gql(query), variables: variables);
// 5GMyvKsTNk9wDBy9jwKaX6mhSzmFFtpdK9KNnmrLoSTSuJHv // 5GMyvKsTNk9wDBy9jwKaX6mhSzmFFtpdK9KNnmrLoSTSuJHv
...@@ -249,12 +286,7 @@ class DuniterIndexer with ChangeNotifier { ...@@ -249,12 +286,7 @@ class DuniterIndexer with ChangeNotifier {
late String finalAmount; late String finalAmount;
final DateTime date = repository[0]; final DateTime date = repository[0];
final dateForm = "${date.day} ${monthsInYear[date.month]!.substring(0, { final dateForm = "${date.day} ${monthsInYear[date.month]!.substring(0, {1, 2, 7, 9}.contains(date.month) ? 4 : 3)}";
1,
2,
7,
9
}.contains(date.month) ? 4 : 3)}";
DateTime normalizeDate(DateTime inputDate) { DateTime normalizeDate(DateTime inputDate) {
return DateTime(inputDate.year, inputDate.month, inputDate.day); return DateTime(inputDate.year, inputDate.month, inputDate.day);
...@@ -264,12 +296,9 @@ class DuniterIndexer with ChangeNotifier { ...@@ -264,12 +296,9 @@ class DuniterIndexer with ChangeNotifier {
DateTime now = DateTime.now(); DateTime now = DateTime.now();
final transactionDate = normalizeDate(date.toLocal()); final transactionDate = normalizeDate(date.toLocal());
final todayDate = normalizeDate(now); final todayDate = normalizeDate(now);
final yesterdayDate = final yesterdayDate = normalizeDate(now.subtract(const Duration(days: 1)));
normalizeDate(now.subtract(const Duration(days: 1))); final isSameWeek = weekNumber(transactionDate) == weekNumber(now) && transactionDate.year == now.year;
final isSameWeek = weekNumber(transactionDate) == weekNumber(now) && final isTodayOrYesterday = transactionDate == todayDate || transactionDate == yesterdayDate;
transactionDate.year == now.year;
final isTodayOrYesterday =
transactionDate == todayDate || transactionDate == yesterdayDate;
if (transactionDate == todayDate) { if (transactionDate == todayDate) {
return "today".tr(); return "today".tr();
...@@ -299,8 +328,7 @@ class DuniterIndexer with ChangeNotifier { ...@@ -299,8 +328,7 @@ class DuniterIndexer with ChangeNotifier {
finalAmount = '$amount $currencyName'; finalAmount = '$amount $currencyName';
} }
bool isMigrationTime = bool isMigrationTime = startBlockchainInitialized && date.compareTo(startBlockchainTime) < 0;
startBlockchainInitialized && date.compareTo(startBlockchainTime) < 0;
return { return {
'finalAmount': finalAmount, 'finalAmount': finalAmount,
......
...@@ -6,6 +6,8 @@ class SearchProvider with ChangeNotifier { ...@@ -6,6 +6,8 @@ class SearchProvider with ChangeNotifier {
final searchController = TextEditingController(); final searchController = TextEditingController();
List searchResult = []; List searchResult = [];
int resultLenght = 0; int resultLenght = 0;
bool canPasteAddress = false;
String pastedAddress = '';
void reload() { void reload() {
notifyListeners(); notifyListeners();
......
...@@ -9,7 +9,6 @@ import 'package:gecko/models/migrate_wallet_checks.dart'; ...@@ -9,7 +9,6 @@ import 'package:gecko/models/migrate_wallet_checks.dart';
import 'package:gecko/models/scale_functions.dart'; import 'package:gecko/models/scale_functions.dart';
import 'package:gecko/models/transaction_content.dart'; import 'package:gecko/models/transaction_content.dart';
import 'package:gecko/models/wallet_data.dart'; import 'package:gecko/models/wallet_data.dart';
import 'package:gecko/providers/duniter_indexer.dart';
import 'package:gecko/providers/home.dart'; import 'package:gecko/providers/home.dart';
import 'package:gecko/providers/my_wallets.dart'; import 'package:gecko/providers/my_wallets.dart';
import 'package:gecko/providers/wallet_options.dart'; import 'package:gecko/providers/wallet_options.dart';
...@@ -64,21 +63,15 @@ class SubstrateSdk with ChangeNotifier { ...@@ -64,21 +63,15 @@ class SubstrateSdk with ChangeNotifier {
'Finalized': TransactionStatus.finalized 'Finalized': TransactionStatus.finalized
}; };
Future _executeCall(TransactionContent transcationContent, TxInfoData txInfo, Future _executeCall(TransactionContent transcationContent, TxInfoData txInfo, txOptions, String password, [String? rawParams]) async {
txOptions, String password, final walletOptions = Provider.of<WalletOptionsProvider>(homeContext, listen: false);
[String? rawParams]) async { final walletProfiles = Provider.of<WalletsProfilesProvider>(homeContext, listen: false);
final walletOptions =
Provider.of<WalletOptionsProvider>(homeContext, listen: false);
final walletProfiles =
Provider.of<WalletsProfilesProvider>(homeContext, listen: false);
final currentTransactionId = transcationContent.transactionId; final currentTransactionId = transcationContent.transactionId;
transactionStatus.putIfAbsent( transactionStatus.putIfAbsent(currentTransactionId, () => transcationContent);
currentTransactionId, () => transcationContent);
notifyListeners(); notifyListeners();
try { try {
final hash = await sdk.api.tx.signAndSend(txInfo, txOptions, password, final hash = await sdk.api.tx.signAndSend(txInfo, txOptions, password, rawParam: rawParams, onStatusChange: (newStatus) {
rawParam: rawParams, onStatusChange: (newStatus) {
transactionStatus.update(currentTransactionId, (trans) { transactionStatus.update(currentTransactionId, (trans) {
trans.status = statusMap[newStatus]!; trans.status = statusMap[newStatus]!;
return trans; return trans;
...@@ -148,9 +141,7 @@ class SubstrateSdk with ChangeNotifier { ...@@ -148,9 +141,7 @@ class SubstrateSdk with ChangeNotifier {
} }
Future<int> _getStorageConst(String call) async { Future<int> _getStorageConst(String call) async {
final result = (await sdk.webView! final result = (await sdk.webView!.evalJavascript('api.consts.$call', wrapPromise: false) ?? [null])[0];
.evalJavascript('api.consts.$call', wrapPromise: false) ??
[null])[0];
return checkInt(result) ?? 0; return checkInt(result) ?? 0;
} }
...@@ -170,8 +161,7 @@ class SubstrateSdk with ChangeNotifier { ...@@ -170,8 +161,7 @@ class SubstrateSdk with ChangeNotifier {
); );
} }
Future<String> _signMessage( Future<String> _signMessage(Uint8List message, String address, String password) async {
Uint8List message, String address, String password) async {
final params = SignAsExtensionParam(); final params = SignAsExtensionParam();
params.msgType = "pub(bytes.sign)"; params.msgType = "pub(bytes.sign)";
params.request = { params.request = {
...@@ -184,12 +174,10 @@ class SubstrateSdk with ChangeNotifier { ...@@ -184,12 +174,10 @@ class SubstrateSdk with ChangeNotifier {
} }
Future<String> signDatapod(String document, String address) async { Future<String> signDatapod(String document, String address) async {
final myWalletProvider = final myWalletProvider = Provider.of<MyWalletsProvider>(homeContext, listen: false);
Provider.of<MyWalletsProvider>(homeContext, listen: false);
final messageToSign = Uint8List.fromList(document.codeUnits); final messageToSign = Uint8List.fromList(document.codeUnits);
final signatureString = final signatureString = await _signMessage(messageToSign, address, myWalletProvider.pinCode);
await _signMessage(messageToSign, address, myWalletProvider.pinCode);
final signatureInt = HEX.decode(signatureString.substring(2)); final signatureInt = HEX.decode(signatureString.substring(2));
final signature64 = base64Encode(signatureInt); final signature64 = base64Encode(signatureInt);
...@@ -206,8 +194,7 @@ class SubstrateSdk with ChangeNotifier { ...@@ -206,8 +194,7 @@ class SubstrateSdk with ChangeNotifier {
Future<List<int?>> _getIdentityIndexOfMulti(List<String> addresses) async { Future<List<int?>> _getIdentityIndexOfMulti(List<String> addresses) async {
String jsonString = jsonEncode(addresses); String jsonString = jsonEncode(addresses);
return List<int?>.from( return List<int?>.from(await _getStorage('identity.identityIndexOf.multi($jsonString)'));
await _getStorage('identity.identityIndexOf.multi($jsonString)'));
} }
Future<List<int>> getCertsCounter(String address) async { Future<List<int>> getCertsCounter(String address) async {
...@@ -216,21 +203,11 @@ class SubstrateSdk with ChangeNotifier { ...@@ -216,21 +203,11 @@ class SubstrateSdk with ChangeNotifier {
certsCounterCache.update(address, (_) => [], ifAbsent: () => []); certsCounterCache.update(address, (_) => [], ifAbsent: () => []);
return []; return [];
} }
final certsReceiver = final certsReceiver = await _getStorage('certification.storageIdtyCertMeta($idtyIndex)') ?? [];
await _getStorage('certification.storageIdtyCertMeta($idtyIndex)') ??
[];
try { try {
certsCounterCache.update( certsCounterCache.update(address, (_) => [certsReceiver['receivedCount'] as int, certsReceiver['issuedCount'] as int],
address, ifAbsent: () => [certsReceiver['receivedCount'] as int, certsReceiver['issuedCount'] as int]);
(_) => [
certsReceiver['receivedCount'] as int,
certsReceiver['issuedCount'] as int
],
ifAbsent: () => [
certsReceiver['receivedCount'] as int,
certsReceiver['issuedCount'] as int
]);
} catch (e) { } catch (e) {
log.e(e); log.e(e);
} }
...@@ -243,8 +220,7 @@ class SubstrateSdk with ChangeNotifier { ...@@ -243,8 +220,7 @@ class SubstrateSdk with ChangeNotifier {
if (idtyIndexFrom == null || idtyIndexTo == null) return 0; if (idtyIndexFrom == null || idtyIndexTo == null) return 0;
final List certData = final List certData = await _getStorage('certification.certsByReceiver($idtyIndexTo)') ?? [];
await _getStorage('certification.certsByReceiver($idtyIndexTo)') ?? [];
if (certData.isEmpty) return 0; if (certData.isEmpty) return 0;
for (List certInfo in certData) { for (List certInfo in certData) {
...@@ -269,13 +245,11 @@ class SubstrateSdk with ChangeNotifier { ...@@ -269,13 +245,11 @@ class SubstrateSdk with ChangeNotifier {
Future<double> getBalanceRatio() async { Future<double> getBalanceRatio() async {
udValue = await getUdValue(); udValue = await getUdValue();
balanceRatio = balanceRatio = (configBox.get('isUdUnit') ?? false) ? round(udValue / 100, 6) : 1;
(configBox.get('isUdUnit') ?? false) ? round(udValue / 100, 6) : 1;
return balanceRatio; return balanceRatio;
} }
Future<Map<String, Map<String, double>>> getBalanceMulti( Future<Map<String, Map<String, double>>> getBalanceMulti(List<String> addresses) async {
List<String> addresses) async {
List stringifyAddresses = []; List stringifyAddresses = [];
for (var element in addresses) { for (var element in addresses) {
stringifyAddresses.add('"$element"'); stringifyAddresses.add('"$element"');
...@@ -283,23 +257,14 @@ class SubstrateSdk with ChangeNotifier { ...@@ -283,23 +257,14 @@ class SubstrateSdk with ChangeNotifier {
// Get onchain storage values // Get onchain storage values
final List<Map> accountMulti = final List<Map> accountMulti =
(await _getStorage('system.account.multi($stringifyAddresses)') as List) (await _getStorage('system.account.multi($stringifyAddresses)') as List).map((dynamic e) => e as Map<String, dynamic>).toList();
.map((dynamic e) => e as Map<String, dynamic>)
.toList();
final List<int?> idtyIndexList = (await _getStorage( final List<int?> idtyIndexList = (await _getStorage('identity.identityIndexOf.multi($stringifyAddresses)') as List).map((dynamic e) => e as int?).toList();
'identity.identityIndexOf.multi($stringifyAddresses)') as List)
.map((dynamic e) => e as int?)
.toList();
//FIXME: With local dev duniter node only, need to switch null values by unused init as index to have good idtyDataList... //FIXME: With local dev duniter node only, need to switch null values by unused init as index to have good idtyDataList...
final List<int> idtyIndexListNoNull = final List<int> idtyIndexListNoNull = idtyIndexList.map((item) => item ?? 99999999).toList();
idtyIndexList.map((item) => item ?? 99999999).toList();
final List<Map?> idtyDataList = (idtyIndexListNoNull.isEmpty final List<Map?> idtyDataList = (idtyIndexListNoNull.isEmpty ? [] : (await _getStorage('identity.identities.multi($idtyIndexListNoNull)')) as List)
? []
: (await _getStorage(
'identity.identities.multi($idtyIndexListNoNull)')) as List)
.map((dynamic e) => e as Map<String, dynamic>?) .map((dynamic e) => e as Map<String, dynamic>?)
.toList(); .toList();
...@@ -326,23 +291,17 @@ class SubstrateSdk with ChangeNotifier { ...@@ -326,23 +291,17 @@ class SubstrateSdk with ChangeNotifier {
// Get onchain storage values // Get onchain storage values
final Map account = await _getStorage('system.account("$address")'); final Map account = await _getStorage('system.account("$address")');
final int? idtyIndex = final int? idtyIndex = await _getStorage('identity.identityIndexOf("$address")');
await _getStorage('identity.identityIndexOf("$address")'); final Map? idtyData = idtyIndex == null ? null : await _getStorage('identity.identities($idtyIndex)');
final Map? idtyData = idtyIndex == null
? null
: await _getStorage('identity.identities($idtyIndex)');
return _computeBalance(idtyData, account); return _computeBalance(idtyData, account);
} }
Future<Map<String, double>> _computeBalance( Future<Map<String, double>> _computeBalance(Map? idtyData, Map account) async {
Map? idtyData, Map account) async { final List pastReevals = await _getStorage('universalDividend.pastReevals()');
final List pastReevals =
await _getStorage('universalDividend.pastReevals()');
// Compute amount of claimable UDs // Compute amount of claimable UDs
currentUdIndex = await getCurrentUdIndex(); currentUdIndex = await getCurrentUdIndex();
final int unclaimedUds = _computeUnclaimUds( final int unclaimedUds = _computeUnclaimUds(idtyData?['data']?['firstEligibleUd'] ?? 0, pastReevals);
idtyData?['data']?['firstEligibleUd'] ?? 0, pastReevals);
// Calculate transferable and potential balance // Calculate transferable and potential balance
final int transferableBalance = (account['data']['free'] + unclaimedUds); final int transferableBalance = (account['data']['free'] + unclaimedUds);
...@@ -397,14 +356,10 @@ class SubstrateSdk with ChangeNotifier { ...@@ -397,14 +356,10 @@ class SubstrateSdk with ChangeNotifier {
return CertState(status: CertStatus.none); return CertState(status: CertStatus.none);
} else if (certRemovableDuration >= renewDelay) { } else if (certRemovableDuration >= renewDelay) {
final certRenewDuration = certRemovableDuration - renewDelay; final certRenewDuration = certRemovableDuration - renewDelay;
return CertState( return CertState(status: CertStatus.canRenewIn, duration: Duration(seconds: certRenewDuration));
status: CertStatus.canRenewIn,
duration: Duration(seconds: certRenewDuration));
} else if (nextIssuableOn > blocNumber) { } else if (nextIssuableOn > blocNumber) {
final certDelayDuration = (nextIssuableOn - blocNumber) * 6; final certDelayDuration = (nextIssuableOn - blocNumber) * 6;
return CertState( return CertState(status: CertStatus.mustWaitBeforeCert, duration: Duration(seconds: certDelayDuration));
status: CertStatus.mustWaitBeforeCert,
duration: Duration(seconds: certDelayDuration));
} else if (toStatus == IdtyStatus.unconfirmed) { } else if (toStatus == IdtyStatus.unconfirmed) {
return CertState(status: CertStatus.mustConfirmIdentity); return CertState(status: CertStatus.mustConfirmIdentity);
} else { } else {
...@@ -415,9 +370,7 @@ class SubstrateSdk with ChangeNotifier { ...@@ -415,9 +370,7 @@ class SubstrateSdk with ChangeNotifier {
Future<Map> getCertMeta(String address) async { Future<Map> getCertMeta(String address) async {
var idtyIndex = await _getIdentityIndexOf(address); var idtyIndex = await _getIdentityIndexOf(address);
final certMeta = final certMeta = await _getStorage('certification.storageIdtyCertMeta($idtyIndex)') ?? '';
await _getStorage('certification.storageIdtyCertMeta($idtyIndex)') ??
'';
return certMeta; return certMeta;
} }
...@@ -454,8 +407,7 @@ class SubstrateSdk with ChangeNotifier { ...@@ -454,8 +407,7 @@ class SubstrateSdk with ChangeNotifier {
//FIXME: should not have to replace null values by 99999999 //FIXME: should not have to replace null values by 99999999
final idtyIndexesFix = idtyIndexes.map((item) => item ?? 99999999).toList(); final idtyIndexesFix = idtyIndexes.map((item) => item ?? 99999999).toList();
final jsonString = jsonEncode(idtyIndexesFix); final jsonString = jsonEncode(idtyIndexesFix);
final List idtyStatusList = final List idtyStatusList = await _getStorage('identity.identities.multi($jsonString)');
await _getStorage('identity.identities.multi($jsonString)');
List<IdtyStatus> resultStatus = []; List<IdtyStatus> resultStatus = [];
final mapStatus = { final mapStatus = {
...@@ -520,15 +472,14 @@ class SubstrateSdk with ChangeNotifier { ...@@ -520,15 +472,14 @@ class SubstrateSdk with ChangeNotifier {
'ss58': 'system.ss58Prefix.words', 'ss58': 'system.ss58Prefix.words',
'minCertForMembership': 'wot.minCertForMembership.words', 'minCertForMembership': 'wot.minCertForMembership.words',
'existentialDeposit': 'balances.existentialDeposit.words', 'existentialDeposit': 'balances.existentialDeposit.words',
'certPeriod': 'cert.certPeriod.words', 'certPeriod': 'certification.certPeriod.words',
'certMaxByIssuer': 'cert.maxByIssuer.words', 'certMaxByIssuer': 'certification.maxByIssuer.words',
'certValidityPeriod': 'cert.validityPeriod.words', 'certValidityPeriod': 'certification.validityPeriod.words',
}; };
for (var param in currencyParametersNames.keys) { for (final param in currencyParametersNames.keys) {
try { try {
currencyParameters[param] = currencyParameters[param] = await _getStorageConst(currencyParametersNames[param]!);
await _getStorageConst(currencyParametersNames[param]!);
} catch (e) { } catch (e) {
log.e('error while getting param $param :: $e'); log.e('error while getting param $param :: $e');
} }
...@@ -541,15 +492,13 @@ class SubstrateSdk with ChangeNotifier { ...@@ -541,15 +492,13 @@ class SubstrateSdk with ChangeNotifier {
notifyListeners(); notifyListeners();
} }
Future<double> txFees( Future<double> txFees(String fromAddress, String destAddress, double amount) async {
String fromAddress, String destAddress, double amount) async {
if (amount == 0) return 0; if (amount == 0) return 0;
final sender = await _setSender(fromAddress); final sender = await _setSender(fromAddress);
final txInfo = TxInfoData('balances', 'transferKeepAlive', sender); final txInfo = TxInfoData('balances', 'transferKeepAlive', sender);
final amountUnit = (amount * 100).toInt(); final amountUnit = (amount * 100).toInt();
final estimateFees = final estimateFees = await sdk.api.tx.estimateFees(txInfo, [destAddress, amountUnit]);
await sdk.api.tx.estimateFees(txInfo, [destAddress, amountUnit]);
return estimateFees.partialFee / 100; return estimateFees.partialFee / 100;
} }
...@@ -625,6 +574,13 @@ class SubstrateSdk with ChangeNotifier { ...@@ -625,6 +574,13 @@ class SubstrateSdk with ChangeNotifier {
return DateFormat(); return DateFormat();
} }
Future<String> getLastFinilizedHash() async => await sdk.webView!.evalJavascript('api.rpc.chain.getFinalizedHead()');
Future<int> getBlockNumberByHash(String hash) async {
final result = await sdk.webView!.evalJavascript('api.rpc.chain.getBlock("$hash")');
return result['block']['header']['number'] as int;
}
///////////////////////////////////// /////////////////////////////////////
////// 3: SUBSTRATE CONNECTION ////// ////// 3: SUBSTRATE CONNECTION //////
///////////////////////////////////// /////////////////////////////////////
...@@ -646,18 +602,12 @@ class SubstrateSdk with ChangeNotifier { ...@@ -646,18 +602,12 @@ class SubstrateSdk with ChangeNotifier {
Future<void> connectNode() async { Future<void> connectNode() async {
final homeProvider = Provider.of<HomeProvider>(homeContext, listen: false); final homeProvider = Provider.of<HomeProvider>(homeContext, listen: false);
final myWalletProvider = final myWalletProvider = Provider.of<MyWalletsProvider>(homeContext, listen: false);
Provider.of<MyWalletsProvider>(homeContext, listen: false);
final duniterIndexer =
Provider.of<DuniterIndexer>(homeContext, listen: false);
homeProvider.changeMessage("connectionPending".tr(), 0); homeProvider.changeMessage("connectionPending".tr(), 0);
// configBox.delete('customEndpoint'); // configBox.delete('customEndpoint');
final List<NetworkParams> listEndpoints = final List<NetworkParams> listEndpoints = configBox.containsKey('customEndpoint') ? [getDuniterCustomEndpoint()] : getDuniterBootstrap();
configBox.containsKey('customEndpoint')
? [getDuniterCustomEndpoint()]
: getDuniterBootstrap();
int timeout = 15; int timeout = 15;
...@@ -693,14 +643,8 @@ class SubstrateSdk with ChangeNotifier { ...@@ -693,14 +643,8 @@ class SubstrateSdk with ChangeNotifier {
// Currency parameters // Currency parameters
await initCurrencyParameters(); await initCurrencyParameters();
// Indexer Blockchain start
duniterIndexer.getBlockStart();
notifyListeners(); notifyListeners();
homeProvider.changeMessage( homeProvider.changeMessage("wellConnectedToNode".tr(args: [getConnectedEndpoint()!.split('/')[2]]), 0);
"wellConnectedToNode"
.tr(args: [getConnectedEndpoint()!.split('/')[2]]),
5);
} else { } else {
nodeConnected = false; nodeConnected = false;
notifyListeners(); notifyListeners();
...@@ -736,11 +680,7 @@ class SubstrateSdk with ChangeNotifier { ...@@ -736,11 +680,7 @@ class SubstrateSdk with ChangeNotifier {
return nodeParams; return nodeParams;
} }
Future<String> importAccount( Future<String> importAccount({String mnemonic = '', String derivePath = '', required String password, CryptoType cryptoType = CryptoType.sr25519}) async {
{String mnemonic = '',
String derivePath = '',
required String password,
CryptoType cryptoType = CryptoType.sr25519}) async {
const keytype = KeyType.mnemonic; const keytype = KeyType.mnemonic;
if (mnemonic != '') generatedMnemonic = mnemonic; if (mnemonic != '') generatedMnemonic = mnemonic;
...@@ -748,13 +688,7 @@ class SubstrateSdk with ChangeNotifier { ...@@ -748,13 +688,7 @@ class SubstrateSdk with ChangeNotifier {
notifyListeners(); notifyListeners();
final json = await sdk.api.keyring final json = await sdk.api.keyring
.importAccount(keyring, .importAccount(keyring, keyType: keytype, key: generatedMnemonic, name: derivePath, password: password, derivePath: derivePath, cryptoType: cryptoType)
keyType: keytype,
key: generatedMnemonic,
name: derivePath,
password: password,
derivePath: derivePath,
cryptoType: cryptoType)
.catchError((e) { .catchError((e) {
importIsLoading = false; importIsLoading = false;
notifyListeners(); notifyListeners();
...@@ -785,8 +719,7 @@ class SubstrateSdk with ChangeNotifier { ...@@ -785,8 +719,7 @@ class SubstrateSdk with ChangeNotifier {
////////////////////////////////// //////////////////////////////////
KeyPairData getKeypair(String address) { KeyPairData getKeypair(String address) {
return keyring.keyPairs.firstWhere((kp) => kp.address == address, return keyring.keyPairs.firstWhere((kp) => kp.address == address, orElse: (() => KeyPairData()));
orElse: (() => KeyPairData()));
} }
Future<bool> checkPassword(String address, String pass) async { Future<bool> checkPassword(String address, String pass) async {
...@@ -811,11 +744,9 @@ class SubstrateSdk with ChangeNotifier { ...@@ -811,11 +744,9 @@ class SubstrateSdk with ChangeNotifier {
return seedText; return seedText;
} }
Future<KeyPairData?> changePassword(BuildContext context, String address, Future<KeyPairData?> changePassword(BuildContext context, String address, String passOld, String passNew) async {
String passOld, String passNew) async {
final account = getKeypair(address); final account = getKeypair(address);
final myWalletProvider = final myWalletProvider = Provider.of<MyWalletsProvider>(context, listen: false);
Provider.of<MyWalletsProvider>(context, listen: false);
keyring.setCurrent(account); keyring.setCurrent(account);
myWalletProvider.debounceResetPinCode(); myWalletProvider.debounceResetPinCode();
...@@ -836,8 +767,7 @@ class SubstrateSdk with ChangeNotifier { ...@@ -836,8 +767,7 @@ class SubstrateSdk with ChangeNotifier {
} }
Future<String> generateMnemonic({String lang = appLang}) async { Future<String> generateMnemonic({String lang = appLang}) async {
final gen = await sdk.api.keyring final gen = await sdk.api.keyring.generateMnemonic(currencyParameters['ss58'] ?? initSs58);
.generateMnemonic(currencyParameters['ss58'] ?? initSs58);
generatedMnemonic = gen.mnemonic!; generatedMnemonic = gen.mnemonic!;
return gen.mnemonic!; return gen.mnemonic!;
...@@ -867,28 +797,22 @@ class SubstrateSdk with ChangeNotifier { ...@@ -867,28 +797,22 @@ class SubstrateSdk with ChangeNotifier {
} }
} }
Future<String> derive( Future<String> derive(BuildContext context, String address, int number, String password) async {
BuildContext context, String address, int number, String password) async {
final keypair = getKeypair(address); final keypair = getKeypair(address);
final seedMap = final seedMap = await keyring.store.getDecryptedSeed(keypair.pubKey, password);
await keyring.store.getDecryptedSeed(keypair.pubKey, password);
if (seedMap?['type'] != 'mnemonic') return ''; if (seedMap?['type'] != 'mnemonic') return '';
final List seedList = seedMap!['seed'].split('//'); final List seedList = seedMap!['seed'].split('//');
generatedMnemonic = seedList[0]; generatedMnemonic = seedList[0];
return await importAccount( return await importAccount(mnemonic: generatedMnemonic, derivePath: '//$number', password: password);
mnemonic: generatedMnemonic,
derivePath: '//$number',
password: password);
} }
Future<String> generateRootKeypair(String address, String password) async { Future<String> generateRootKeypair(String address, String password) async {
final keypair = getKeypair(address); final keypair = getKeypair(address);
final seedMap = final seedMap = await keyring.store.getDecryptedSeed(keypair.pubKey, password);
await keyring.store.getDecryptedSeed(keypair.pubKey, password);
if (seedMap?['type'] != 'mnemonic') return ''; if (seedMap?['type'] != 'mnemonic') return '';
final List seedList = seedMap!['seed'].split('//'); final List seedList = seedMap!['seed'].split('//');
...@@ -921,10 +845,7 @@ class SubstrateSdk with ChangeNotifier { ...@@ -921,10 +845,7 @@ class SubstrateSdk with ChangeNotifier {
final rawSeedHex = '0x${HEX.encode(rawSeed)}'; final rawSeedHex = '0x${HEX.encode(rawSeed)}';
// Just get the address without keystore // Just get the address without keystore
final newAddress = await sdk.api.keyring.addressFromRawSeed( final newAddress = await sdk.api.keyring.addressFromRawSeed(currencyParameters['ss58']!, cryptoType: CryptoType.ed25519, rawSeed: rawSeedHex);
currencyParameters['ss58']!,
cryptoType: CryptoType.ed25519,
rawSeed: rawSeedHex);
SigningKey rootKey = SigningKey(seed: rawSeed); SigningKey rootKey = SigningKey(seed: rawSeed);
g1V1OldPubkey = Base58Encode(rootKey.publicKey); g1V1OldPubkey = Base58Encode(rootKey.publicKey);
...@@ -934,21 +855,17 @@ class SubstrateSdk with ChangeNotifier { ...@@ -934,21 +855,17 @@ class SubstrateSdk with ChangeNotifier {
return g1V1NewAddress; return g1V1NewAddress;
} }
Future<MigrateWalletChecks> getBalanceAndIdtyStatus( Future<MigrateWalletChecks> getBalanceAndIdtyStatus(String fromAddress, String toAddress) async {
String fromAddress, String toAddress) async {
bool canValidate = false; bool canValidate = false;
String validationStatus = ''; String validationStatus = '';
final fromBalance = fromAddress == '' final fromBalance = fromAddress == '' ? {'transferableBalance': 0} : await getBalance(fromAddress);
? {'transferableBalance': 0}
: await getBalance(fromAddress);
final transferableBalance = fromBalance['transferableBalance']; final transferableBalance = fromBalance['transferableBalance'];
final statusList = await idtyStatus([fromAddress, toAddress]); final statusList = await idtyStatus([fromAddress, toAddress]);
final fromIdtyStatus = statusList[0]; final fromIdtyStatus = statusList[0];
final fromHasConsumer = final fromHasConsumer = fromAddress == '' ? false : await hasAccountConsumers(fromAddress);
fromAddress == '' ? false : await hasAccountConsumers(fromAddress);
final toIdtyStatus = statusList[1]; final toIdtyStatus = statusList[1];
final isSmithData = await isSmith(fromAddress); final isSmithData = await isSmith(fromAddress);
...@@ -959,11 +876,9 @@ class SubstrateSdk with ChangeNotifier { ...@@ -959,11 +876,9 @@ class SubstrateSdk with ChangeNotifier {
validationStatus = 'youMustWaitBeforeCashoutThisAccount'.tr(); validationStatus = 'youMustWaitBeforeCashoutThisAccount'.tr();
} else if (transferableBalance == 0) { } else if (transferableBalance == 0) {
validationStatus = 'thisAccountIsEmpty'.tr(); validationStatus = 'thisAccountIsEmpty'.tr();
} else if (toIdtyStatus != IdtyStatus.none && } else if (toIdtyStatus != IdtyStatus.none && fromIdtyStatus != IdtyStatus.none) {
fromIdtyStatus != IdtyStatus.none) {
validationStatus = 'youCannotMigrateIdentityToExistingIdentity'.tr(); validationStatus = 'youCannotMigrateIdentityToExistingIdentity'.tr();
} else if (fromIdtyStatus == IdtyStatus.none || } else if (fromIdtyStatus == IdtyStatus.none || toIdtyStatus == IdtyStatus.none) {
toIdtyStatus == IdtyStatus.none) {
canValidate = true; canValidate = true;
} }
...@@ -980,11 +895,7 @@ class SubstrateSdk with ChangeNotifier { ...@@ -980,11 +895,7 @@ class SubstrateSdk with ChangeNotifier {
///////// 5: CALLS EXECUTION ///////// ///////// 5: CALLS EXECUTION /////////
////////////////////////////////////// //////////////////////////////////////
Future<String> pay( Future<String> pay({required String fromAddress, required String destAddress, required double amount, required String password}) async {
{required String fromAddress,
required String destAddress,
required double amount,
required String password}) async {
final sender = await _setSender(fromAddress); final sender = await _setSender(fromAddress);
final globalBalance = await getBalance(fromAddress); final globalBalance = await getBalance(fromAddress);
...@@ -1042,8 +953,7 @@ class SubstrateSdk with ChangeNotifier { ...@@ -1042,8 +953,7 @@ class SubstrateSdk with ChangeNotifier {
return transactionId; return transactionId;
} }
Future<String> certify( Future<String> certify(String fromAddress, String destAddress, String password) async {
String fromAddress, String destAddress, String password) async {
final statusList = await idtyStatus([fromAddress, destAddress]); final statusList = await idtyStatus([fromAddress, destAddress]);
final myIdtyStatus = statusList[0]; final myIdtyStatus = statusList[0];
final toIdtyStatus = statusList[1]; final toIdtyStatus = statusList[1];
...@@ -1063,8 +973,7 @@ class SubstrateSdk with ChangeNotifier { ...@@ -1063,8 +973,7 @@ class SubstrateSdk with ChangeNotifier {
if (toCerts.isEmpty) { if (toCerts.isEmpty) {
toCerts = [0, 0]; toCerts = [0, 0];
} }
log.d( log.d("debug toCert: ${toCerts[0]} --- ${currencyParameters['minCertForMembership']!} --- $toIdtyStatus");
"debug toCert: ${toCerts[0]} --- ${currencyParameters['minCertForMembership']!} --- $toIdtyStatus");
if (toIdtyStatus == IdtyStatus.none) { if (toIdtyStatus == IdtyStatus.none) {
txInfo = TxInfoData( txInfo = TxInfoData(
...@@ -1073,10 +982,8 @@ class SubstrateSdk with ChangeNotifier { ...@@ -1073,10 +982,8 @@ class SubstrateSdk with ChangeNotifier {
sender, sender,
); );
txOptions = [destAddress]; txOptions = [destAddress];
} else if (toIdtyStatus == IdtyStatus.member || } else if (toIdtyStatus == IdtyStatus.member || toIdtyStatus == IdtyStatus.unvalidated) {
toIdtyStatus == IdtyStatus.unvalidated) { if (toCerts[0] >= currencyParameters['minCertForMembership']! - 1 && toIdtyStatus != IdtyStatus.member) {
if (toCerts[0] >= currencyParameters['minCertForMembership']! - 1 &&
toIdtyStatus != IdtyStatus.member) {
log.d('Batch cert and membership validation'); log.d('Batch cert and membership validation');
txInfo = TxInfoData( txInfo = TxInfoData(
'utility', 'utility',
...@@ -1113,8 +1020,7 @@ class SubstrateSdk with ChangeNotifier { ...@@ -1113,8 +1020,7 @@ class SubstrateSdk with ChangeNotifier {
return transactionId; return transactionId;
} }
Future<String> confirmIdentity( Future<String> confirmIdentity(String fromAddress, String name, String password) async {
String fromAddress, String name, String password) async {
final sender = await _setSender(fromAddress); final sender = await _setSender(fromAddress);
final txInfo = TxInfoData( final txInfo = TxInfoData(
...@@ -1155,11 +1061,9 @@ class SubstrateSdk with ChangeNotifier { ...@@ -1155,11 +1061,9 @@ class SubstrateSdk with ChangeNotifier {
final genesisHash = HEX.decode(genesisHashString.substring(2)) as Uint8List; final genesisHash = HEX.decode(genesisHashString.substring(2)) as Uint8List;
final idtyIndex = _int32bytes((await _getIdentityIndexOf(fromAddress))!); final idtyIndex = _int32bytes((await _getIdentityIndexOf(fromAddress))!);
final oldPubkey = await addressToPubkey(fromAddress); final oldPubkey = await addressToPubkey(fromAddress);
final messageToSign = final messageToSign = Uint8List.fromList(prefix + genesisHash + idtyIndex + oldPubkey);
Uint8List.fromList(prefix + genesisHash + idtyIndex + oldPubkey);
final messageToSignHex = HEX.encode(messageToSign); final messageToSignHex = HEX.encode(messageToSign);
final newKeySig = final newKeySig = await _signMessage(messageToSign, destAddress, destPassword);
await _signMessage(messageToSign, destAddress, destPassword);
final newKeySigType = '{"Sr25519": "$newKeySig"}'; final newKeySigType = '{"Sr25519": "$newKeySig"}';
log.d(""" log.d("""
...@@ -1184,13 +1088,10 @@ newKeySig: $newKeySigType"""); ...@@ -1184,13 +1088,10 @@ newKeySig: $newKeySigType""");
); );
const tx1 = 'api.tx.universalDividend.claimUds()'; const tx1 = 'api.tx.universalDividend.claimUds()';
final tx2 = final tx2 = 'api.tx.identity.changeOwnerKey("$destAddress", $newKeySigType)';
'api.tx.identity.changeOwnerKey("$destAddress", $newKeySigType)';
final tx3 = 'api.tx.balances.transferAll("$destAddress", false)'; final tx3 = 'api.tx.balances.transferAll("$destAddress", false)';
rawParams = fromBalance['unclaimedUds'] == 0 rawParams = fromBalance['unclaimedUds'] == 0 ? '[[$tx2, $tx3]]' : '[[$tx1, $tx2, $tx3]]';
? '[[$tx2, $tx3]]'
: '[[$tx1, $tx2, $tx3]]';
} else { } else {
txInfo = TxInfoData( txInfo = TxInfoData(
'identity', 'identity',
...@@ -1209,8 +1110,7 @@ newKeySig: $newKeySigType"""); ...@@ -1209,8 +1110,7 @@ newKeySig: $newKeySigType""");
to: fromAddress, to: fromAddress,
amount: -1, amount: -1,
); );
_executeCall( _executeCall(transactionContent, txInfo, txOptions, fromPassword, rawParams);
transactionContent, txInfo, txOptions, fromPassword, rawParams);
return transactionId; return transactionId;
} }
...@@ -1222,10 +1122,8 @@ newKeySig: $newKeySigType"""); ...@@ -1222,10 +1122,8 @@ newKeySig: $newKeySigType""");
final genesisHashString = await getGenesisHash(); final genesisHashString = await getGenesisHash();
final genesisHash = HEX.decode(genesisHashString.substring(2)) as Uint8List; final genesisHash = HEX.decode(genesisHashString.substring(2)) as Uint8List;
final idtyIndexBytes = _int32bytes(idtyIndex!); final idtyIndexBytes = _int32bytes(idtyIndex!);
final messageToSign = final messageToSign = Uint8List.fromList(prefix + genesisHash + idtyIndexBytes);
Uint8List.fromList(prefix + genesisHash + idtyIndexBytes); final revocationSig = (await _signMessage(messageToSign, address, password)).substring(2);
final revocationSig =
(await _signMessage(messageToSign, address, password)).substring(2);
final revocationSigTyped = '0x01$revocationSig'; final revocationSigTyped = '0x01$revocationSig';
final txInfo = TxInfoData( final txInfo = TxInfoData(
...@@ -1290,11 +1188,7 @@ newKeySig: $newKeySigType"""); ...@@ -1290,11 +1188,7 @@ newKeySig: $newKeySigType""");
late String transactionId; late String transactionId;
if (fromIdtyStatus == IdtyStatus.none) { if (fromIdtyStatus == IdtyStatus.none) {
transactionId = await pay( transactionId = await pay(fromAddress: keypair.address!, destAddress: destAddress, amount: -1, password: 'password');
fromAddress: keypair.address!,
destAddress: destAddress,
amount: -1,
password: 'password');
} else if (fromBalance['transferableBalance'] != 0) { } else if (fromBalance['transferableBalance'] != 0) {
transactionId = await migrateIdentity( transactionId = await migrateIdentity(
fromAddress: keypair.address!, fromAddress: keypair.address!,
...@@ -1317,8 +1211,7 @@ newKeySig: $newKeySigType"""); ...@@ -1317,8 +1211,7 @@ newKeySig: $newKeySigType""");
number = until - blocNumber; number = until - blocNumber;
} }
for (var i = 1; i <= number; i++) { for (var i = 1; i <= number; i++) {
await sdk.webView! await sdk.webView!.evalJavascript('api.rpc.engine.createBlock(true, true)');
.evalJavascript('api.rpc.engine.createBlock(true, true)');
} }
} }
...@@ -1338,8 +1231,7 @@ void snackNode(bool isConnected) { ...@@ -1338,8 +1231,7 @@ void snackNode(bool isConnected) {
} else { } else {
final sub = Provider.of<SubstrateSdk>(homeContext, listen: false); final sub = Provider.of<SubstrateSdk>(homeContext, listen: false);
message = message = "${"youAreConnectedToNode".tr()}\n${sub.getConnectedEndpoint()!.split('//')[1]}";
"${"youAreConnectedToNode".tr()}\n${sub.getConnectedEndpoint()!.split('//')[1]}";
} }
final snackBar = SnackBar( final snackBar = SnackBar(
backgroundColor: Colors.grey[900], backgroundColor: Colors.grey[900],
...@@ -1350,15 +1242,12 @@ void snackNode(bool isConnected) { ...@@ -1350,15 +1242,12 @@ void snackNode(bool isConnected) {
} }
String getShortPubkey(String pubkey) { String getShortPubkey(String pubkey) {
String pubkeyShort = truncate(pubkey, 7, String pubkeyShort = truncate(pubkey, 7, omission: String.fromCharCode(0x2026), position: TruncatePosition.end) +
omission: String.fromCharCode(0x2026),
position: TruncatePosition.end) +
truncate(pubkey, 6, omission: "", position: TruncatePosition.start); truncate(pubkey, 6, omission: "", position: TruncatePosition.start);
return pubkeyShort; return pubkeyShort;
} }
Uint8List _int32bytes(int value) => Uint8List _int32bytes(int value) => Uint8List(4)..buffer.asInt32List()[0] = value;
Uint8List(4)..buffer.asInt32List()[0] = value;
double round(double number, [int decimal = 2]) { double round(double number, [int decimal = 2]) {
return double.parse((number.toStringAsFixed(decimal))); return double.parse((number.toStringAsFixed(decimal)));
......
...@@ -37,10 +37,8 @@ class _HomeScreenState extends State<HomeScreen> { ...@@ -37,10 +37,8 @@ class _HomeScreenState extends State<HomeScreen> {
WidgetsBinding.instance.addPostFrameCallback((_) async { WidgetsBinding.instance.addPostFrameCallback((_) async {
final homeProvider = Provider.of<HomeProvider>(context, listen: false); final homeProvider = Provider.of<HomeProvider>(context, listen: false);
final sub = Provider.of<SubstrateSdk>(context, listen: false); final sub = Provider.of<SubstrateSdk>(context, listen: false);
final duniterIndexer = final duniterIndexer = Provider.of<DuniterIndexer>(context, listen: false);
Provider.of<DuniterIndexer>(context, listen: false); final myWalletProvider = Provider.of<MyWalletsProvider>(context, listen: false);
final myWalletProvider =
Provider.of<MyWalletsProvider>(context, listen: false);
final datapod = Provider.of<V2sDatapodProvider>(context, listen: false); final datapod = Provider.of<V2sDatapodProvider>(context, listen: false);
final bool isWalletsExists = myWalletProvider.isWalletsExists(); final bool isWalletsExists = myWalletProvider.isWalletsExists();
...@@ -77,35 +75,12 @@ class _HomeScreenState extends State<HomeScreen> { ...@@ -77,35 +75,12 @@ class _HomeScreenState extends State<HomeScreen> {
homeProvider.isWalletBoxInit = true; homeProvider.isWalletBoxInit = true;
myWalletProvider.reload(); myWalletProvider.reload();
duniterIndexer.getValidIndexerEndpoint().then((validIndexerEndpoint) {
final wsLinkIndexer = WebSocketLink(
'wss://$validIndexerEndpoint/v1beta1/relay',
);
// const headerWebsocket =
// datapodEndpoint == '10.0.2.2:8080' ? 'ws' : 'wss';
// final wsLinkDatapod = WebSocketLink(
// '$headerWebsocket://$datapodEndpoint/v1/graphql',
// );
duniterIndexer.indexerClient = GraphQLClient(
cache: GraphQLCache(),
link: wsLinkIndexer,
);
// datapod.datapodClient = GraphQLClient(
// cache: GraphQLCache(),
// link: wsLinkDatapod,
// );
});
await homeProvider.getValidEndpoints(); await homeProvider.getValidEndpoints();
if (configBox.get('isCacheChecked') == null) { if (configBox.get('isCacheChecked') == null) {
configBox.put('isCacheChecked', false); configBox.put('isCacheChecked', false);
} }
Future<void> updateConnectionStatus( Future<void> updateConnectionStatus(List<ConnectivityResult> result) async {
List<ConnectivityResult> result) async {
log.i('Network changed: $result'); log.i('Network changed: $result');
if (result.contains(ConnectivityResult.none)) { if (result.contains(ConnectivityResult.none)) {
sub.nodeConnected = false; sub.nodeConnected = false;
...@@ -117,6 +92,22 @@ class _HomeScreenState extends State<HomeScreen> { ...@@ -117,6 +92,22 @@ class _HomeScreenState extends State<HomeScreen> {
var connectivityResult = await (Connectivity().checkConnectivity()); var connectivityResult = await (Connectivity().checkConnectivity());
if (!connectivityResult.contains(ConnectivityResult.none)) { if (!connectivityResult.contains(ConnectivityResult.none)) {
await sub.connectNode(); await sub.connectNode();
//Connect to Indexer
final validIndexerEndpoint = await duniterIndexer.getValidIndexerEndpoint();
final wsLinkIndexer = WebSocketLink(
'wss://$validIndexerEndpoint/v1beta1/relay',
);
duniterIndexer.indexerClient = GraphQLClient(
cache: GraphQLCache(),
link: wsLinkIndexer,
);
// Indexer Blockchain start
duniterIndexer.getBlockStart();
homeProvider.changeMessage("Node and indexer synced !".tr(), 5);
} }
} }
} }
...@@ -135,9 +126,7 @@ class _HomeScreenState extends State<HomeScreen> { ...@@ -135,9 +126,7 @@ class _HomeScreenState extends State<HomeScreen> {
Provider.of<ChestProvider>(context); Provider.of<ChestProvider>(context);
final isWalletsExists = myWalletProvider.isWalletsExists(); final isWalletsExists = myWalletProvider.isWalletsExists();
isTall = (MediaQuery.of(context).size.height / isTall = (MediaQuery.of(context).size.height / MediaQuery.of(context).size.width) > 1.75;
MediaQuery.of(context).size.width) >
1.75;
return Scaffold( return Scaffold(
resizeToAvoidBottomInset: false, resizeToAvoidBottomInset: false,
...@@ -158,8 +147,7 @@ Widget geckHome(context) { ...@@ -158,8 +147,7 @@ Widget geckHome(context) {
fit: BoxFit.cover, fit: BoxFit.cover,
), ),
), ),
child: child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[
Column(crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[
Stack(children: <Widget>[ Stack(children: <Widget>[
Positioned( Positioned(
top: statusBarHeight + scaleSize(10), top: statusBarHeight + scaleSize(10),
...@@ -177,15 +165,12 @@ Widget geckHome(context) { ...@@ -177,15 +165,12 @@ Widget geckHome(context) {
), ),
), ),
Align( Align(
child: Image( child: Image(image: const AssetImage('assets/home/header.png'), height: scaleSize(165)),
image: const AssetImage('assets/home/header.png'),
height: scaleSize(165)),
), ),
]), ]),
Padding( Padding(
padding: const EdgeInsets.only(top: 15), padding: const EdgeInsets.only(top: 15),
child: child: Row(mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[
Row(mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[
DefaultTextStyle( DefaultTextStyle(
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: scaledTextStyle( style: scaledTextStyle(
...@@ -246,8 +231,7 @@ Widget welcomeHome(context) { ...@@ -246,8 +231,7 @@ Widget welcomeHome(context) {
fit: BoxFit.cover, fit: BoxFit.cover,
), ),
), ),
child: child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[
Column(crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[
Stack(children: <Widget>[ Stack(children: <Widget>[
Positioned( Positioned(
top: statusBarHeight + scaleSize(10), top: statusBarHeight + scaleSize(10),
...@@ -265,15 +249,12 @@ Widget welcomeHome(context) { ...@@ -265,15 +249,12 @@ Widget welcomeHome(context) {
), ),
), ),
Align( Align(
child: Image( child: Image(image: const AssetImage('assets/home/header.png'), height: scaleSize(165)),
image: const AssetImage('assets/home/header.png'),
height: scaleSize(165)),
), ),
]), ]),
Padding( Padding(
padding: const EdgeInsets.only(top: 1), padding: const EdgeInsets.only(top: 1),
child: child: Row(mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[
Row(mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[
Text( Text(
"fastAppDescription".tr(args: [currencyName]), "fastAppDescription".tr(args: [currencyName]),
textAlign: TextAlign.center, textAlign: TextAlign.center,
...@@ -321,8 +302,7 @@ Widget welcomeHome(context) { ...@@ -321,8 +302,7 @@ Widget welcomeHome(context) {
Padding( Padding(
padding: EdgeInsets.only(top: scaleSize(55)), padding: EdgeInsets.only(top: scaleSize(55)),
child: Image( child: Image(
image: const AssetImage( image: const AssetImage('assets/home/gecko-bienvenue.png'),
'assets/home/gecko-bienvenue.png'),
height: scaleSize(180), height: scaleSize(180),
), ),
), ),
...@@ -356,10 +336,7 @@ Widget welcomeHome(context) { ...@@ -356,10 +336,7 @@ Widget welcomeHome(context) {
}, },
child: Text( child: Text(
'createWallet'.tr(), 'createWallet'.tr(),
style: scaledTextStyle( style: scaledTextStyle(fontSize: 20, fontWeight: FontWeight.w600, color: Colors.white),
fontSize: 20,
fontWeight: FontWeight.w600,
color: Colors.white),
), ),
), ),
), ),
...@@ -369,8 +346,7 @@ Widget welcomeHome(context) { ...@@ -369,8 +346,7 @@ Widget welcomeHome(context) {
height: 60, height: 60,
child: OutlinedButton( child: OutlinedButton(
key: keyRestoreChest, key: keyRestoreChest,
style: OutlinedButton.styleFrom( style: OutlinedButton.styleFrom(side: BorderSide(width: scaleSize(4), color: orangeC)),
side: BorderSide(width: scaleSize(4), color: orangeC)),
onPressed: () { onPressed: () {
Navigator.push( Navigator.push(
context, context,
...@@ -383,10 +359,7 @@ Widget welcomeHome(context) { ...@@ -383,10 +359,7 @@ Widget welcomeHome(context) {
}, },
child: Text( child: Text(
"restoreWallet".tr(), "restoreWallet".tr(),
style: scaledTextStyle( style: scaledTextStyle(fontSize: 20, color: orangeC, fontWeight: FontWeight.w600),
fontSize: 20,
color: orangeC,
fontWeight: FontWeight.w600),
), ),
), ),
), ),
......
...@@ -2,16 +2,15 @@ ...@@ -2,16 +2,15 @@
import 'dart:async'; import 'dart:async';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/services.dart';
import 'package:gecko/globals.dart'; import 'package:gecko/globals.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:gecko/models/scale_functions.dart'; import 'package:gecko/models/scale_functions.dart';
import 'package:gecko/models/widgets_keys.dart'; import 'package:gecko/models/widgets_keys.dart';
import 'package:gecko/providers/search.dart'; import 'package:gecko/providers/search.dart';
import 'package:gecko/providers/wallets_profiles.dart';
import 'package:gecko/screens/my_contacts.dart'; import 'package:gecko/screens/my_contacts.dart';
import 'package:gecko/screens/search_result.dart'; import 'package:gecko/screens/search_result.dart';
import 'package:gecko/screens/wallet_view.dart'; import 'package:gecko/screens/wallet_view.dart';
import 'package:gecko/widgets/clipboard_monitor.dart';
import 'package:gecko/widgets/commons/offline_info.dart'; import 'package:gecko/widgets/commons/offline_info.dart';
import 'package:gecko/widgets/commons/top_appbar.dart'; import 'package:gecko/widgets/commons/top_appbar.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
...@@ -26,47 +25,13 @@ class SearchScreen extends StatefulWidget { ...@@ -26,47 +25,13 @@ class SearchScreen extends StatefulWidget {
class _SearchScreenState extends State<SearchScreen> { class _SearchScreenState extends State<SearchScreen> {
Timer? debounce; Timer? debounce;
final int debouneTime = 50; final int debouneTime = 50;
bool canPasteAddress = false;
String pastedAddress = '';
Timer? clipboardPollingTimer;
void getClipBoard() {
final searchProvider = Provider.of<SearchProvider>(context, listen: false);
// Function to check clipboard and update if necessary
Future<void> checkAndUpdateClipboard() async {
final clipboardData = await Clipboard.getData(Clipboard.kTextPlain);
if (clipboardData?.text?.isEmpty ?? true) return;
if (clipboardData!.text != pastedAddress) {
canPasteAddress = await isAddress(clipboardData.text!);
if (!canPasteAddress) return;
pastedAddress = clipboardData.text!;
searchProvider.reload();
}
}
// Check clipboard immediately
checkAndUpdateClipboard();
// Set up the periodic clipboard checking
clipboardPollingTimer =
Timer.periodic(const Duration(milliseconds: 500), (_) async {
await checkAndUpdateClipboard();
});
}
@override @override
void initState() { void initState() {
getClipBoard(); ClipboardMonitor().startMonitoring();
super.initState(); super.initState();
} }
@override
void dispose() {
clipboardPollingTimer?.cancel();
super.dispose();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final searchProvider = Provider.of<SearchProvider>(context); final searchProvider = Provider.of<SearchProvider>(context);
...@@ -139,13 +104,9 @@ class _SearchScreenState extends State<SearchScreen> { ...@@ -139,13 +104,9 @@ class _SearchScreenState extends State<SearchScreen> {
height: scaleSize(10), height: scaleSize(10),
), ),
), ),
border: OutlineInputBorder( border: OutlineInputBorder(borderSide: BorderSide(color: Colors.grey[500]!, width: 2), borderRadius: BorderRadius.circular(8)),
borderSide:
BorderSide(color: Colors.grey[500]!, width: 2),
borderRadius: BorderRadius.circular(8)),
focusedBorder: OutlineInputBorder( focusedBorder: OutlineInputBorder(
borderSide: borderSide: BorderSide(color: Colors.grey[500]!, width: 2.5),
BorderSide(color: Colors.grey[500]!, width: 2.5),
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
), ),
contentPadding: const EdgeInsets.all(13), contentPadding: const EdgeInsets.all(13),
...@@ -177,13 +138,12 @@ class _SearchScreenState extends State<SearchScreen> { ...@@ -177,13 +138,12 @@ class _SearchScreenState extends State<SearchScreen> {
}), }),
); );
} }
: canPasteAddress : searchProvider.canPasteAddress
? () async { ? () async {
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute(builder: (context) { MaterialPageRoute(builder: (context) {
return WalletViewScreen( return WalletViewScreen(address: searchProvider.pastedAddress, username: null);
address: pastedAddress, username: null);
}), }),
); );
} }
...@@ -191,14 +151,11 @@ class _SearchScreenState extends State<SearchScreen> { ...@@ -191,14 +151,11 @@ class _SearchScreenState extends State<SearchScreen> {
child: Text( child: Text(
canValidate canValidate
? 'search'.tr() ? 'search'.tr()
: canPasteAddress : searchProvider.canPasteAddress
? 'pasteAddress'.tr() ? 'pasteAddress'.tr()
: 'search'.tr(), : 'search'.tr(),
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: scaledTextStyle( style: scaledTextStyle(fontSize: 16, fontWeight: FontWeight.w500, color: Colors.white),
fontSize: 16,
fontWeight: FontWeight.w500,
color: Colors.white),
), ),
), ),
), ),
......
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:gecko/globals.dart';
import 'package:gecko/providers/search.dart';
import 'package:gecko/providers/wallets_profiles.dart';
import 'package:provider/provider.dart';
class ClipboardMonitor extends ChangeNotifier {
String? _lastClipboardContent;
Timer? _debounceTimer;
final searchProvider = Provider.of<SearchProvider>(homeContext, listen: false);
void startMonitoring() {
_checkClipboard();
}
void _checkClipboard() async {
final clipboardData = await Clipboard.getData(Clipboard.kTextPlain);
final newContent = clipboardData?.text;
if (newContent != null && newContent != _lastClipboardContent) {
_lastClipboardContent = newContent;
_debounceTimer?.cancel();
_debounceTimer = Timer(const Duration(milliseconds: 300), () async {
if (await isAddress(newContent)) {
searchProvider.pastedAddress = newContent;
searchProvider.canPasteAddress = true;
searchProvider.reload();
} else {
searchProvider.pastedAddress = '';
searchProvider.canPasteAddress = false;
searchProvider.reload();
}
});
}
Future.delayed(const Duration(seconds: 1), _checkClipboard);
}
@override
void dispose() {
_debounceTimer?.cancel();
searchProvider.canPasteAddress = false;
super.dispose();
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please to comment