diff --git a/assets/skull_Icon.png b/assets/skull_Icon.png new file mode 100755 index 0000000..3a789fa Binary files /dev/null and b/assets/skull_Icon.png differ diff --git a/assets/translations/en.json b/assets/translations/en.json index 2b3929c..d3d510a 100644 --- a/assets/translations/en.json +++ b/assets/translations/en.json @@ -147,6 +147,7 @@ "execTimeoutOver": "Execution timeout is over", "seeAWallet": "See a wallet", "mustWaitXBeforeCertify": "You have to wait\n{} before\ncertifying again", + "mustConfirmHisIdentity": "This person must confirm\nhis identity before can be\ncertified", "canRenewCertInX": "You can renew\nthis certification\nin {}", "executeATransfer": "Execute a transfer", "executeTheTransfer": "Execute the transfer", @@ -174,5 +175,18 @@ "noContacts": "You don't have any contact", "addContact": "Add\nto contacts", "removeContact": "Remove\nthis contact", - "derivationsScanProgress": "Scan address {}/{}" + "derivationsScanProgress": "Scan address {}/{}", + "youAreOffline": "You are offline...", + "importG1v1": "Import old G1v1 account", + "selectDestWallet": "Select a target wallet:", + "youMustWaitBeforeCashoutThisAccount": "You have to wait {} minutes\nbefore migrate this account", + "thisAccountIsEmpty": "This account is empty", + "youCannotMigrateIdentityToExistingIdentity": "You cannot migrate an identity\nto an account that already has an identity", + "importOldAccount": "Import your old account", + "enterCesiumId": "Enter your Cesium ID", + "enterCesiumPassword": "Enter your Cesium password", + "migrateAccount": "Migrate account", + "migrateIdentity": "Migrate identity", + "identityMigration": "Identity migration", + "areYouSureMigrateIdentity": "Are you sure you want to permanently migrate identity **{}** with balance of **{}** ?" } \ No newline at end of file diff --git a/assets/translations/es.json b/assets/translations/es.json index 56533d0..8994665 100644 --- a/assets/translations/es.json +++ b/assets/translations/es.json @@ -147,6 +147,7 @@ "execTimeoutOver": "Execution timeout is over", "seeAWallet": "See a wallet", "mustWaitXBeforeCertify": "You have to wait\n{} before\ncertifying again", + "mustConfirmHisIdentity": "This person must confirm\nhis identity before can be\ncertified", "canRenewCertInX": "You can renew\nthis certification\nin {}", "executeATransfer": "Execute a transfer", "executeTheTransfer": "Execute the transfer", @@ -174,5 +175,18 @@ "noContacts": "You don't have any contact", "addContact": "Add\nto contacts", "removeContact": "Remove\nthis contact", - "derivationsScanProgress": "Scan address {}/{}" + "derivationsScanProgress": "Scan address {}/{}", + "youAreOffline": "You are offline...", + "importG1v1": "Import old G1v1 account", + "selectDestWallet": "Select a target wallet:", + "youMustWaitBeforeCashoutThisAccount": "You have to wait {} minutes\nbefore migrate this account", + "thisAccountIsEmpty": "This account is empty", + "youCannotMigrateIdentityToExistingIdentity": "You cannot migrate an identity\nto an account that already has an identity", + "importOldAccount": "Import your old account", + "enterCesiumId": "Enter your Cesium ID", + "enterCesiumPassword": "Enter your Cesium password", + "migrateAccount": "Migrate account", + "migrateIdentity": "Migrate identity", + "identityMigration": "Identity migration", + "areYouSureMigrateIdentity": "Are you sure you want to permanently migrate identity **{}** with balance of **{}** ?" } \ No newline at end of file diff --git a/assets/translations/fr.json b/assets/translations/fr.json index 188027a..ba9d0f5 100644 --- a/assets/translations/fr.json +++ b/assets/translations/fr.json @@ -148,6 +148,7 @@ "execTimeoutOver": "Le délais d'éxecution est dépassé", "seeAWallet": "Voir un portefeuille", "mustWaitXBeforeCertify": "Vous devez attendre\n{} avant\nde pouvoir certifier", + "mustConfirmHisIdentity": "Cette personne doit confirmer\nson identité avant de pouvoir\nêtre certifié", "canRenewCertInX": "Vous pourrez renouveller\ncette certification\ndans {}", "executeATransfer": "Effectuer un virement", "executeTheTransfer": "Effectuer le virement", @@ -175,5 +176,18 @@ "noContacts": "Vous n'avez aucun contact", "addContact": "Ajouter\naux contacts", "removeContact": "Supprimer\nce contact", - "derivationsScanProgress": "Scan de l'adresse {}/{}" + "derivationsScanProgress": "Scan de l'adresse {}/{}", + "youAreOffline": "Vous êtes hors ligne...", + "importG1v1": "Importer un ancien compte G1v1", + "selectDestWallet": "Sélectionnez un portefeuille cible:", + "youMustWaitBeforeCashoutThisAccount": "Vous devez attendre {} minutes\navant de pouvoir migrer ce compte", + "thisAccountIsEmpty": "Ce compte est vide", + "youCannotMigrateIdentityToExistingIdentity": "Vous ne pouvez pas migrer une identité\nvers un compte disposant déjà d'une identité", + "importOldAccount": "Importer son ancien compte", + "enterCesiumId": "Entrez votre identifiant Cesium", + "enterCesiumPassword": "Entrez votre mot de passe Cesium", + "migrateAccount": "Migrer le compte", + "migrateIdentity": "Migrer l'identité", + "identityMigration": "Migration de l'identité", + "areYouSureMigrateIdentity": "Êtes-vous certain de vouloir migrer définitivement l'identité **{}** et son solde de **{}** ?" } \ No newline at end of file diff --git a/lib/providers/generate_wallets.dart b/lib/providers/generate_wallets.dart index b47e463..24aef78 100644 --- a/lib/providers/generate_wallets.dart +++ b/lib/providers/generate_wallets.dart @@ -390,7 +390,7 @@ class GenerateWalletsProvider with ChangeNotifier { for (var derivationNbr in [for (var i = 0; i < numberScan; i += 1) i]) { final addressData = await sub.sdk.api.keyring.addressFromMnemonic( - sub.ss58, + sub.currencyParameters['ss58']!, cryptoType: CryptoType.sr25519, mnemonic: generatedMnemonic!, derivePath: '//$derivationNbr'); @@ -409,7 +409,9 @@ class GenerateWalletsProvider with ChangeNotifier { ? 'currentWallet'.tr() : '${'wallet'.tr()} ${scanedValidWalletNumber + 1}'; await sub.importAccount( - derivePath: '//$derivationNbr', password: pin.text); + mnemonic: generatedMnemonic!, + derivePath: '//$derivationNbr', + password: pin.text); WalletData myWallet = WalletData( version: dataVersion, @@ -433,8 +435,10 @@ class GenerateWalletsProvider with ChangeNotifier { } Future scanRootBalance(SubstrateSdk sub, int currentChestNumber) async { - final addressData = await sub.sdk.api.keyring.addressFromMnemonic(sub.ss58, - cryptoType: CryptoType.sr25519, mnemonic: generatedMnemonic!); + final addressData = await sub.sdk.api.keyring.addressFromMnemonic( + sub.currencyParameters['ss58']!, + cryptoType: CryptoType.sr25519, + mnemonic: generatedMnemonic!); final balance = await sub.getBalance(addressData.address!).timeout( const Duration(seconds: 1), @@ -445,7 +449,7 @@ class GenerateWalletsProvider with ChangeNotifier { "${addressData.address!}: ${balance['transferableBalance']} $currencyName"); if (balance['transferableBalance'] != 0) { String walletName = 'myRootWallet'.tr(); - await sub.importAccount(password: pin.text); + await sub.importAccount(mnemonic: generatedMnemonic!, password: pin.text); WalletData myWallet = WalletData( version: dataVersion, diff --git a/lib/providers/substrate_sdk.dart b/lib/providers/substrate_sdk.dart index 4d476dd..13a85cf 100644 --- a/lib/providers/substrate_sdk.dart +++ b/lib/providers/substrate_sdk.dart @@ -1,3 +1,6 @@ +// ignore_for_file: use_build_context_synchronously + +import 'dart:typed_data'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:gecko/globals.dart'; @@ -11,9 +14,11 @@ import 'package:polkawallet_sdk/api/types/txInfoData.dart'; import 'package:polkawallet_sdk/polkawallet_sdk.dart'; import 'package:polkawallet_sdk/storage/keyring.dart'; import 'package:polkawallet_sdk/storage/types/keyPairData.dart'; +import 'package:polkawallet_sdk/webviewWithExtension/types/signExtrinsicParam.dart'; import 'package:provider/provider.dart'; import 'package:truncate/truncate.dart'; -// import 'package:web_socket_channel/io.dart'; +import 'package:pointycastle/pointycastle.dart' as pc; +import "package:hex/hex.dart"; class SubstrateSdk with ChangeNotifier { final WalletSDK sdk = WalletSDK(); @@ -27,13 +32,18 @@ class SubstrateSdk with ChangeNotifier { bool isLoadingEndpoint = false; String debugConnection = ''; String transactionStatus = ''; - int ss58 = 42; + final int initSs58 = 42; + Map currencyParameters = {}; + TextEditingController csSalt = TextEditingController(); + TextEditingController csPassword = TextEditingController(); + String g1V1NewAddress = ''; + bool isCesiumIDVisible = true; ///////////////////////////////////// ////////// 1: API METHODS /////////// ///////////////////////////////////// - Future executeCall(TxInfoData txInfo, txOptions, String password, + Future _executeCall(TxInfoData txInfo, txOptions, String password, [String? rawParams]) async { try { final hash = await sdk.api.tx @@ -60,10 +70,16 @@ class SubstrateSdk with ChangeNotifier { } } - Future getStorage(String call) async { + Future _getStorage(String call) async { return await sdk.webView!.evalJavascript('api.query.$call'); } + Future _getStorageConst(String call) async { + return (await sdk.webView! + .evalJavascript('api.consts.$call', wrapPromise: false) ?? + [null])[0]; + } + TxSenderData _setSender() { return TxSenderData( keyring.current.address, @@ -71,30 +87,43 @@ class SubstrateSdk with ChangeNotifier { ); } + Future _signMessage( + Uint8List message, String address, String password) async { + final params = SignAsExtensionParam(); + params.msgType = "pub(bytes.sign)"; + params.request = { + "address": address, + "data": message, + }; + + final res = await sdk.api.keyring.signAsExtension(password, params); + return res?.signature ?? ''; + } + //////////////////////////////////////////// ////////// 2: GET ONCHAIN STORAGE ////////// //////////////////////////////////////////// - Future getIdentityIndexOf(String address) async { - return await getStorage('identity.identityIndexOf("$address")') ?? 0; + Future _getIdentityIndexOf(String address) async { + return await _getStorage('identity.identityIndexOf("$address")') ?? 0; } Future> getCerts(String address) async { - final idtyIndex = await getIdentityIndexOf(address); + final idtyIndex = await _getIdentityIndexOf(address); final certsReceiver = - await getStorage('cert.storageIdtyCertMeta($idtyIndex)') ?? []; + await _getStorage('cert.storageIdtyCertMeta($idtyIndex)') ?? []; return [certsReceiver['receivedCount'], certsReceiver['issuedCount']]; } Future getCertValidityPeriod(String from, String to) async { - final idtyIndexFrom = await getIdentityIndexOf(from); - final idtyIndexTo = await getIdentityIndexOf(to); + 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)') ?? []; + await _getStorage('cert.certsByReceiver($idtyIndexTo)') ?? []; if (certData.isEmpty) return 0; for (List certInfo in certData) { @@ -106,14 +135,8 @@ class SubstrateSdk with ChangeNotifier { return 0; } - Future> getParameters() async { - final currencyParameters = - await getStorage('parameters.parametersStorage()') ?? {}; - return currencyParameters; - } - Future hasAccountConsumers(String address) async { - final accountInfo = await getStorage('system.account("$address")'); + final accountInfo = await _getStorage('system.account("$address")'); final consumers = accountInfo['consumers']; return consumers == 0 ? false : true; } @@ -134,6 +157,8 @@ class SubstrateSdk with ChangeNotifier { // } Future> getBalance(String address) async { + // log.d('currencyParameters: $currencyParameters'); + if (!nodeConnected) { return { 'transferableBalance': 0, @@ -144,16 +169,16 @@ class SubstrateSdk with ChangeNotifier { } // Get onchain storage values - final Map balanceGlobal = await getStorage('system.account("$address")'); + final Map balanceGlobal = await _getStorage('system.account("$address")'); final int? idtyIndex = - await getStorage('identity.identityIndexOf("$address")'); + await _getStorage('identity.identityIndexOf("$address")'); final Map? idtyData = idtyIndex == null ? null - : await getStorage('identity.identities($idtyIndex)'); + : await _getStorage('identity.identities($idtyIndex)'); final int currentUdIndex = - int.parse(await getStorage('universalDividend.currentUdIndex()')); + int.parse(await _getStorage('universalDividend.currentUdIndex()')); final List pastReevals = - await getStorage('universalDividend.pastReevals()'); + await _getStorage('universalDividend.pastReevals()'); // Compute amount of claimable UDs final int unclaimedUds = _computeUnclaimUds(currentUdIndex, @@ -200,23 +225,14 @@ class SubstrateSdk with ChangeNotifier { return totalAmount; } - Future 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 isMemberGet(String address) async { return await idtyStatus(address) == 'Validated'; } Future> certState(String from, String to) async { Map result = {}; + final toStatus = await idtyStatus(to); + if (from != to && await isMemberGet(from)) { final removableOn = await getCertValidityPeriod(from, to); final certMeta = await getCertMeta(from); @@ -230,32 +246,35 @@ class SubstrateSdk with ChangeNotifier { } else if (nextIssuableOn > blocNumber) { final certDelayDuration = (nextIssuableOn - blocNumber) * 6; result.putIfAbsent('certDelay', () => certDelayDuration); + } else if (toStatus == 'Created') { + result.putIfAbsent('toStatus', () => 1); } else { result.putIfAbsent('canCert', () => 0); } - // log.d('tatatatata: ${nextIssuableOn - blocNumber}'); } + // if (toStatus == 'Created') result.putIfAbsent('toStatus', () => 1); + return result; } Future getCertMeta(String address) async { - var idtyIndex = await getIdentityIndexOf(address); + var idtyIndex = await _getIdentityIndexOf(address); final certMeta = - await getStorage('cert.storageIdtyCertMeta($idtyIndex)') ?? ''; + await _getStorage('cert.storageIdtyCertMeta($idtyIndex)') ?? ''; return certMeta; } - Future idtyStatus(String address, [bool smooth = true]) async { - var idtyIndex = await getIdentityIndexOf(address); + Future idtyStatus(String address) async { + var idtyIndex = await _getIdentityIndexOf(address); if (idtyIndex == 0) { return 'noid'; } - final idtyStatus = await getStorage('identity.identities($idtyIndex)'); + final idtyStatus = await _getStorage('identity.identities($idtyIndex)'); if (idtyStatus != null) { final String status = idtyStatus['status']; @@ -266,7 +285,53 @@ class SubstrateSdk with ChangeNotifier { } } - Future getCurencyName() async {} + Future getGenesisHash() async { + final String genesisHash = await sdk.webView!.evalJavascript( + 'api.genesisHash.toHex()', + wrapPromise: false, + ) ?? + []; + // log.d('genesisHash: $genesisHash'); + // log.d('genesisHash: ${HEX.decode(genesisHash.substring(2))}'); + return genesisHash; + } + + Future addressToPubkey(String address) async { + final pubkey = await sdk.api.account.decodeAddress([address]); + final String pubkeyHex = pubkey!.keys.first; + final pubkeyByte = HEX.decode(pubkeyHex.substring(2)) as Uint8List; + // final pubkey58 = Base58Encode(pubkeyByte); + + return pubkeyByte; + } + + // 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'); + } + + void cesiumIDisVisible() { + isCesiumIDVisible = !isCesiumIDVisible; + notifyListeners(); + } ///////////////////////////////////// ////// 3: SUBSTRATE CONNECTION ////// @@ -274,8 +339,8 @@ class SubstrateSdk with ChangeNotifier { Future initApi() async { sdkLoading = true; - await keyring.init([ss58]); - keyring.setSS58(ss58); + await keyring.init([initSs58]); + keyring.setSS58(initSs58); await sdk.init(keyring); sdkReady = true; @@ -289,6 +354,8 @@ class SubstrateSdk with ChangeNotifier { Future connectNode(BuildContext ctx) async { HomeProvider homeProvider = Provider.of(ctx, listen: false); + MyWalletsProvider myWalletProvider = + Provider.of(ctx, listen: false); homeProvider.changeMessage("connectionPending".tr(), 0); @@ -329,7 +396,7 @@ class SubstrateSdk with ChangeNotifier { notifyListeners(); }); - // currencyName = await getCurencyName(); + await initCurrencyParameters(); notifyListeners(); homeProvider.changeMessage( "wellConnectedToNode" @@ -341,7 +408,7 @@ class SubstrateSdk with ChangeNotifier { debugConnection = res.toString(); notifyListeners(); homeProvider.changeMessage("noDuniterEndointAvailable".tr(), 0); - // snackNode(ctx, false); + if (!myWalletProvider.checkIfWalletExist()) snackNode(homeContext, false); } log.d(sdk.api.connectedNode?.endpoint); @@ -354,7 +421,7 @@ class SubstrateSdk with ChangeNotifier { final n = NetworkParams(); n.name = currencyName; n.endpoint = endpoint; - n.ss58 = ss58; + n.ss58 = currencyParameters['ss58'] ?? initSs58; node.add(n); } return node; @@ -364,7 +431,7 @@ class SubstrateSdk with ChangeNotifier { final nodeParams = NetworkParams(); nodeParams.name = currencyName; nodeParams.endpoint = configBox.get('customEndpoint'); - nodeParams.ss58 = ss58; + nodeParams.ss58 = currencyParameters['ss58'] ?? initSs58; return nodeParams; } @@ -474,7 +541,8 @@ class SubstrateSdk with ChangeNotifier { } Future generateMnemonic({String lang = appLang}) async { - final gen = await sdk.api.keyring.generateMnemonic(ss58); + final gen = await sdk.api.keyring + .generateMnemonic(currencyParameters['ss58'] ?? initSs58); generatedMnemonic = gen.mnemonic!; return gen.mnemonic!; @@ -508,9 +576,6 @@ class SubstrateSdk with ChangeNotifier { BuildContext context, String address, int number, String password) async { final keypair = getKeypair(address); - //TODO: fix null keypair after used chest import - log.d('tatatata $address $number $password ${keypair.encoded}'); - final seedMap = await keyring.store.getDecryptedSeed(keypair.pubKey, password); @@ -545,6 +610,46 @@ class SubstrateSdk with ChangeNotifier { return await sdk.api.keyring.checkMnemonicValid(mnemonic); } + Future csToV2Address(String salt, String password) 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)}'; + + // Just get the address without keystore + final newAddress = await sdk.api.keyring.addressFromRawSeed( + currencyParameters['ss58']!, + cryptoType: CryptoType.ed25519, + rawSeed: rawSeedHex); + + g1V1NewAddress = newAddress.address!; + notifyListeners(); + return g1V1NewAddress; + } + + Future getBalanceAndIdtyStatus( + String fromAddress, String toAddress) async { + final fromBalance = fromAddress == '' + ? {'transferableBalance': 0} + : await getBalance(fromAddress); + final fromIdtyStatus = + fromAddress == '' ? 'noid' : await idtyStatus(fromAddress); + final fromHasConsumer = + fromAddress == '' ? false : await hasAccountConsumers(fromAddress); + final toIdtyStatus = await idtyStatus(toAddress); + + return [fromBalance, fromIdtyStatus, toIdtyStatus, fromHasConsumer]; + } + ////////////////////////////////////// ///////// 5: CALLS EXECUTION ///////// ////////////////////////////////////// @@ -587,7 +692,7 @@ class SubstrateSdk with ChangeNotifier { } // log.d('pay args: ${txInfo.module}, ${txInfo.call}, $txOptions, $rawParams'); - return await executeCall(txInfo, txOptions, password, rawParams); + return await _executeCall(txInfo, txOptions, password, rawParams); } Future certify( @@ -597,8 +702,8 @@ class SubstrateSdk with ChangeNotifier { final myIdtyStatus = await idtyStatus(fromAddress); final toIdtyStatus = await idtyStatus(toAddress); - final fromIndex = await getIdentityIndexOf(fromAddress); - final toIndex = await getIdentityIndexOf(toAddress); + final fromIndex = await _getIdentityIndexOf(fromAddress); + final toIndex = await _getIdentityIndexOf(toAddress); if (myIdtyStatus != 'Validated') { transactionStatus = 'notMember'; @@ -612,7 +717,8 @@ class SubstrateSdk with ChangeNotifier { String? rawParams; final toCerts = await getCerts(toAddress); - final currencyParameters = await getParameters(); + + // log.d('debug: ${currencyParameters['minCertForMembership']}'); if (toIdtyStatus == 'noid') { txInfo = TxInfoData( @@ -623,7 +729,7 @@ class SubstrateSdk with ChangeNotifier { txOptions = [toAddress]; } else if (toIdtyStatus == 'Validated' || toIdtyStatus == 'ConfirmedByOwner') { - if (toCerts[0] >= currencyParameters['wotMinCertForMembership'] && + if (toCerts[0] >= currencyParameters['minCertForMembership']! - 1 && toIdtyStatus != 'Validated') { log.i('Batch cert and membership validation'); txInfo = TxInfoData( @@ -631,8 +737,8 @@ class SubstrateSdk with ChangeNotifier { 'batchAll', sender, ); - final tx1 = 'cert.addCert($fromIndex, $toIndex)'; - final tx2 = 'identity.validateIdentity($toIndex)'; + final tx1 = 'api.tx.cert.addCert($fromIndex, $toIndex)'; + final tx2 = 'api.tx.identity.validateIdentity($toIndex)'; rawParams = '[[$tx1, $tx2]]'; } else { @@ -650,7 +756,7 @@ class SubstrateSdk with ChangeNotifier { } log.d('Cert action: ${txInfo.call!}'); - return await executeCall(txInfo, txOptions, password, rawParams); + return await _executeCall(txInfo, txOptions, password, rawParams); } // Future claimUDs(String password) async { @@ -684,11 +790,83 @@ class SubstrateSdk with ChangeNotifier { ); final txOptions = [name]; - return await executeCall(txInfo, txOptions, password); + return await _executeCall(txInfo, txOptions, password); + } + + Future migrateIdentity( + {required String fromAddress, + required String destAddress, + required String fromPassword, + required String destPassword, + required Map fromBalance, + bool withBalance = false}) async { + transactionStatus = ''; + final fromPubkey = await sdk.api.account.decodeAddress([fromAddress]); + final sender = TxSenderData( + fromAddress, + fromPubkey!.keys.first, + ); + + TxInfoData txInfo; + List txOptions = []; + String? rawParams; + + final prefix = 'icok'.codeUnits; + final genesisHashString = await getGenesisHash(); + final genesisHash = HEX.decode(genesisHashString.substring(2)) as Uint8List; + final idtyIndex = _int32bytes(await _getIdentityIndexOf(fromAddress)); + final oldPubkey = await addressToPubkey(fromAddress); + final messageToSign = + Uint8List.fromList(prefix + genesisHash + idtyIndex + oldPubkey); + final messageToSignHex = HEX.encode(messageToSign); + final newKeySig = + await _signMessage(messageToSign, destAddress, destPassword); + + // messageToSign: [105, 99, 111, 107, 7, 193, 18, 255, 106, 185, 215, 208, 213, 49, 235, 229, 159, 152, 179, 83, 24, 178, 129, 59, 22, 85, 87, 115, 128, 129, 157, 56, 214, 24, 45, 153, 21, 0, 0, 0, 181, 82, 178, 99, 198, 4, 156, 190, 78, 35, 102, 137, 255, 7, 162, 31, 16, 79, 255, 132, 130, 237, 230, 222, 176, 88, 245, 217, 237, 78, 196, 239] + + log.d(""" +fromAddress: $fromAddress +destAddress: $destAddress +genesisHashString: $genesisHashString + +prefix: $prefix +genesisHash: $genesisHash +idtyIndex: $idtyIndex +oldPubkey: $oldPubkey + +messageToSign: $messageToSign +messageToSignHex: $messageToSignHex +newKeySig: $newKeySig"""); + + if (withBalance) { + 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 = + fromBalance['unclaimedUds'] == 0 ? '[[$tx2]]' : '[[$tx1, $tx2]]'; + } else { + txInfo = TxInfoData( + 'identity', + 'changeOwnerKey', + sender, + ); + + txOptions = [destAddress, newKeySig]; + } + + return await _executeCall(txInfo, txOptions, fromPassword, rawParams); } Future revokeIdentity(String address, String password) async { - final idtyIndex = await getIdentityIndexOf(address); + final idtyIndex = await _getIdentityIndexOf(address); final sender = TxSenderData( keyring.current.address, @@ -706,7 +884,60 @@ class SubstrateSdk with ChangeNotifier { final txOptions = [idtyIndex]; - return await executeCall(txInfo, txOptions, password); + return await _executeCall(txInfo, txOptions, password); + } + + Future migrateCsToV2(String salt, String password, String destAddress, + {required destPassword, + required Map 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, + ); + + log.d('g1migration idtyStatus: $idtyStatus'); + if (idtyStatus != 'noid') { + await migrateIdentity( + fromAddress: keypair.address!, + destAddress: destAddress, + fromPassword: 'password', + destPassword: destPassword, + withBalance: true, + fromBalance: balance); + } else if (balance['transferableBalance'] != 0) { + await pay( + fromAddress: keypair.address!, + destAddress: destAddress, + amount: -1, + password: 'password'); + } + + await sdk.api.keyring.deleteAccount(keyring, keypair); } void reload() { @@ -734,8 +965,7 @@ class AddressInfo { void snackNode(BuildContext context, bool isConnected) { String message; if (!isConnected) { - message = - "${"noDuniterNodeAvailableTryLater".tr()}:\n${configBox.get('endpoint').first}"; + message = "noDuniterNodeAvailableTryLater".tr(); } else { SubstrateSdk sub = Provider.of(context, listen: false); @@ -761,3 +991,6 @@ class PasswordException implements Exception { String cause; PasswordException(this.cause); } + +Uint8List _int32bytes(int value) => + Uint8List(4)..buffer.asInt32List()[0] = value; diff --git a/lib/providers/wallet_options.dart b/lib/providers/wallet_options.dart index fa0c421..bb8a6ea 100644 --- a/lib/providers/wallet_options.dart +++ b/lib/providers/wallet_options.dart @@ -30,6 +30,7 @@ class WalletOptionsProvider with ChangeNotifier { TextEditingController nameController = TextEditingController(); late bool isDefaultWallet; bool canValidateNameBool = false; + Map idtyStatusCache = {}; Future? get badWallet => null; @@ -157,6 +158,7 @@ class WalletOptionsProvider with ChangeNotifier { future: sub.idtyStatus(address), initialData: '', builder: (context, snapshot) { + idtyStatusCache[address] = snapshot.data.toString(); switch (snapshot.data.toString()) { case 'noid': { @@ -300,8 +302,11 @@ class WalletOptionsProvider with ChangeNotifier { Navigator.push( context, MaterialPageRoute(builder: (context) { - return const TransactionInProgress( - transType: 'comfirmIdty'); + return TransactionInProgress( + transType: 'comfirmIdty', + fromAddress: wallet.address, + toAddress: wallet.address, + ); }), ); } diff --git a/lib/providers/wallets_profiles.dart b/lib/providers/wallets_profiles.dart index f1fcc50..ce181c5 100644 --- a/lib/providers/wallets_profiles.dart +++ b/lib/providers/wallets_profiles.dart @@ -138,10 +138,9 @@ class WalletsProfilesProvider with ChangeNotifier { Provider.of(context, listen: false); // SubstrateSdk _sub = Provider.of(context, listen: false); - bool isAccountExist = balanceCache[address] != 0; - return Stack(children: [ Consumer(builder: (context, sub, _) { + bool isAccountExist = balanceCache[address] != 0; return Container( height: 180, decoration: BoxDecoration( diff --git a/lib/screens/common_elements.dart b/lib/screens/common_elements.dart index b104e67..59149f7 100644 --- a/lib/screens/common_elements.dart +++ b/lib/screens/common_elements.dart @@ -159,8 +159,7 @@ class CommonElements { } Widget offlineInfo(BuildContext context) { - // SubstrateSdk _sub = Provider.of(context, listen: false); - final double screenWidth = MediaQuery.of(context).size.width; + final double screenWidth = MediaQuery.of(homeContext).size.width; return Consumer(builder: (context, sub, _) { return Visibility( visible: !sub.nodeConnected, @@ -174,7 +173,7 @@ class CommonElements { mainAxisAlignment: MainAxisAlignment.center, children: [ Text( - "Vous êtes hors ligne...", + 'youAreOffline'.tr(), style: TextStyle(color: Colors.grey[50]), textAlign: TextAlign.center, ), diff --git a/lib/screens/myWallets/import_g1_v1.dart b/lib/screens/myWallets/import_g1_v1.dart new file mode 100644 index 0000000..0bb14fe --- /dev/null +++ b/lib/screens/myWallets/import_g1_v1.dart @@ -0,0 +1,299 @@ +// ignore_for_file: use_build_context_synchronously + +import 'dart:async'; + +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/services.dart'; +import 'package:gecko/globals.dart'; +import 'package:flutter/material.dart'; +import 'package:gecko/models/wallet_data.dart'; +import 'package:gecko/providers/my_wallets.dart'; +import 'package:gecko/providers/substrate_sdk.dart'; +import 'package:gecko/providers/wallet_options.dart'; +import 'package:gecko/screens/myWallets/unlocking_wallet.dart'; +import 'package:gecko/screens/transaction_in_progress.dart'; +import 'package:provider/provider.dart'; + +class ImportG1v1 extends StatelessWidget { + const ImportG1v1({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]); + // HomeProvider _homeProvider = Provider.of(context); + WalletOptionsProvider walletOptions = + Provider.of(context, listen: false); + MyWalletsProvider myWalletProvider = + Provider.of(context, listen: false); + + Timer? debounce; + const int debouneTime = 300; + WalletData selectedWallet = myWalletProvider.getDefaultWallet(); + bool canValidate = false; + String validationStatus = ''; + + return WillPopScope( + onWillPop: () { + resetScreen(context); + return Future.value(true); + }, + child: Scaffold( + backgroundColor: backgroundColor, + appBar: AppBar( + toolbarHeight: 60 * ratio, + leading: IconButton( + icon: const Icon(Icons.arrow_back, color: Colors.black), + onPressed: () { + resetScreen(context); + + Navigator.of(context).pop(); + }), + title: SizedBox( + height: 22, + child: Text('importOldAccount'.tr()), + )), + body: SafeArea( + child: Consumer(builder: (context, sub, _) { + return FutureBuilder( + future: sub.getBalanceAndIdtyStatus( + sub.g1V1NewAddress, selectedWallet.address!), + builder: (BuildContext context, AsyncSnapshot status) { + // log.d(_certs.data); + + if (status.data == null) { + return Column(children: [ + const SizedBox(height: 80), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + SizedBox( + height: 35, + width: 35, + child: CircularProgressIndicator( + color: orangeC, + strokeWidth: 4, + ), + ), + ]), + ]); + } + + final Map balance = status.data?[0] ?? {}; + final String idtyStatus = status.data?[1]; + final String myIdtyStatus = status.data?[2]; + final bool hasConsumer = status.data?[3] ?? false; + + // log.d('hasconsumer: $hasConsumer'); + + if (balance['transferableBalance'] != 0 && !hasConsumer) { + canValidate = true; + validationStatus = ''; + } else { + canValidate = false; + validationStatus = hasConsumer + ? 'youMustWaitBeforeCashoutThisAccount'.tr(args: ['X']) + : 'thisAccountIsEmpty'.tr(); + } + + if (idtyStatus != 'noid' && myIdtyStatus != 'noid') { + canValidate = false; + validationStatus = + 'youCannotMigrateIdentityToExistingIdentity'.tr(); + } + + if (sub.g1V1NewAddress == '') { + validationStatus = ''; + } + + return Column(children: [ + const SizedBox(height: 20), + TextFormField( + autofocus: true, + onChanged: (text) { + if (debounce?.isActive ?? false) { + debounce!.cancel(); + } + debounce = Timer( + const Duration(milliseconds: debouneTime), () { + sub.csToV2Address( + sub.csSalt.text, sub.csPassword.text); + }); + }, + keyboardType: TextInputType.text, + controller: sub.csSalt, + obscureText: sub + .isCesiumIDVisible, //This will obscure text dynamically + decoration: InputDecoration( + hintText: 'enterCesiumId'.tr(), + suffixIcon: IconButton( + icon: Icon( + sub.isCesiumIDVisible + ? Icons.visibility + : Icons.visibility_off, + color: Colors.black, + ), + onPressed: () { + sub.cesiumIDisVisible(); + }, + ), + ), + ), + const SizedBox(height: 20), + TextFormField( + autofocus: true, + onChanged: (text) { + if (debounce?.isActive ?? false) { + debounce!.cancel(); + } + debounce = Timer( + const Duration(milliseconds: debouneTime), () { + sub.csToV2Address( + sub.csSalt.text, sub.csPassword.text); + }); + }, + keyboardType: TextInputType.text, + controller: sub.csPassword, + obscureText: sub + .isCesiumIDVisible, //This will obscure text dynamically + decoration: InputDecoration( + hintText: 'enterCesiumPassword'.tr(), + suffixIcon: IconButton( + icon: Icon( + sub.isCesiumIDVisible + ? Icons.visibility + : Icons.visibility_off, + color: Colors.black, + ), + onPressed: () { + sub.cesiumIDisVisible(); + }, + ), + ), + ), + const SizedBox(height: 20), + Text( + getShortPubkey(sub.g1V1NewAddress), + style: const TextStyle( + fontSize: 18, + color: Colors.black, + fontWeight: FontWeight.bold, + fontFamily: 'Monospace'), + ), + const SizedBox(height: 20), + Text( + '${balance['transferableBalance']} $currencyName', + style: const TextStyle(fontSize: 17), + ), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + walletOptions.idtyStatus(context, sub.g1V1NewAddress, + isOwner: false, color: Colors.black), + const SizedBox(width: 10), + getCerts(context, sub.g1V1NewAddress, 14) + ], + ), + const SizedBox(height: 30), + Text('selectDestWallet'.tr()), + const SizedBox(height: 5), + DropdownButtonHideUnderline( + child: DropdownButton( + // alignment: AlignmentDirectional.topStart, + value: selectedWallet, + icon: const Icon(Icons.keyboard_arrow_down), + items: myWalletProvider.listWallets.map((wallet) { + return DropdownMenuItem( + value: wallet, + child: Text( + wallet.name!, + style: const TextStyle(fontSize: 18), + ), + ); + }).toList(), + onChanged: (WalletData? newSelectedWallet) { + selectedWallet = newSelectedWallet!; + sub.reload(); + }, + ), + ), + const SizedBox(height: 30), + SizedBox( + width: 380 * ratio, + height: 60 * ratio, + child: ElevatedButton( + style: ElevatedButton.styleFrom( + elevation: 4, + primary: orangeC, // background + onPrimary: Colors.white, // foreground + ), + onPressed: canValidate + ? () async { + log.d('GOOO'); + WalletData? defaultWallet = + myWalletProvider.getDefaultWallet(); + + String? pin; + if (myWalletProvider.pinCode == '') { + pin = await Navigator.push( + context, + MaterialPageRoute( + builder: (homeContext) { + return UnlockingWallet( + wallet: defaultWallet); + }, + ), + ); + } + + sub.migrateCsToV2( + sub.csSalt.text, + sub.csPassword.text, + selectedWallet.address!, + destPassword: + pin ?? myWalletProvider.pinCode, + balance: balance, + idtyStatus: idtyStatus); + Navigator.push( + context, + MaterialPageRoute(builder: (context) { + return TransactionInProgress( + transType: 'identityMigration', + fromAddress: + getShortPubkey(sub.g1V1NewAddress), + toAddress: getShortPubkey( + selectedWallet.address!)); + }), + ); + resetScreen(context); + } + : null, + child: Text( + 'migrateAccount'.tr(), + style: TextStyle( + fontSize: 23 * ratio, + fontWeight: FontWeight.w600), + ), + ), + ), + const SizedBox(height: 10), + Text( + validationStatus, + textAlign: TextAlign.center, + style: TextStyle(fontSize: 15, color: Colors.grey[600]), + ) + ]); + }); + }), + ), + ), + ); + } + + void resetScreen(BuildContext context) { + SubstrateSdk sub = Provider.of(context, listen: false); + + sub.csSalt.text = ''; + sub.csPassword.text = ''; + sub.g1V1NewAddress = ''; + } +} diff --git a/lib/screens/myWallets/manage_membership.dart b/lib/screens/myWallets/manage_membership.dart index 5f4759f..4a05ec0 100644 --- a/lib/screens/myWallets/manage_membership.dart +++ b/lib/screens/myWallets/manage_membership.dart @@ -1,6 +1,8 @@ +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/services.dart'; import 'package:gecko/globals.dart'; import 'package:flutter/material.dart'; +import 'package:gecko/screens/myWallets/migrate_identity.dart'; // import 'package:gecko/models/wallet_data.dart'; // import 'package:gecko/providers/my_wallets.dart'; // import 'package:gecko/providers/substrate_sdk.dart'; @@ -22,19 +24,44 @@ class ManageMembership extends StatelessWidget { backgroundColor: backgroundColor, appBar: AppBar( toolbarHeight: 60 * ratio, - title: const SizedBox( + title: SizedBox( height: 22, - child: Text('manageMembership'), + child: const Text('manageMembership').tr(), )), body: SafeArea( child: Column(children: [ const SizedBox(height: 20), - revokeMyIdentity(context), + migrateIdentity(context), + const SizedBox(height: 10), + revokeMyIdentity(context) // const SizedBox(height: 20), ]), )); } + Widget migrateIdentity(BuildContext context) { + return InkWell( + key: const Key('migrateIdentity'), + onTap: () async { + Navigator.push( + context, + MaterialPageRoute(builder: (context) { + return const MigrateIdentityScreen(); + }), + ); + }, + child: SizedBox( + height: 60, + child: Row(children: const [ + SizedBox(width: 16), + Icon(Icons.change_circle_outlined, size: 35), + SizedBox(width: 11.5), + Text('Migrer mon identité', style: TextStyle(fontSize: 20)), + ]), + ), + ); + } + Widget revokeMyIdentity(BuildContext context) { return InkWell( key: const Key('revokeIdty'), @@ -79,14 +106,15 @@ class ManageMembership extends StatelessWidget { // } }, child: SizedBox( - height: 40, - child: Row(children: const [ - SizedBox(width: 32), - // Image.asset( - // 'assets/medal.png', - // height: 45, - // ), - Text('Révoquer mon adhésion', style: TextStyle(fontSize: 20)), + height: 60, + child: Row(children: [ + const SizedBox(width: 20), + Image.asset( + 'assets/skull_Icon.png', + height: 30, + ), + const SizedBox(width: 16), + const Text('Révoquer mon adhésion', style: TextStyle(fontSize: 20)), ]), ), ); diff --git a/lib/screens/myWallets/migrate_identity.dart b/lib/screens/myWallets/migrate_identity.dart new file mode 100644 index 0000000..b8fa00d --- /dev/null +++ b/lib/screens/myWallets/migrate_identity.dart @@ -0,0 +1,252 @@ +// ignore_for_file: use_build_context_synchronously + +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_markdown/flutter_markdown.dart'; +import 'package:gecko/globals.dart'; +import 'package:flutter/material.dart'; +import 'package:gecko/models/wallet_data.dart'; +import 'package:gecko/providers/duniter_indexer.dart'; +import 'package:gecko/providers/my_wallets.dart'; +import 'package:gecko/providers/substrate_sdk.dart'; +import 'package:gecko/providers/wallet_options.dart'; +import 'package:gecko/screens/myWallets/unlocking_wallet.dart'; +import 'package:gecko/screens/transaction_in_progress.dart'; +import 'package:provider/provider.dart'; + +class MigrateIdentityScreen extends StatelessWidget { + const MigrateIdentityScreen({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]); + // HomeProvider _homeProvider = Provider.of(context); + WalletOptionsProvider walletOptions = + Provider.of(context, listen: false); + MyWalletsProvider myWalletProvider = + Provider.of(context, listen: false); + DuniterIndexer duniterIndexer = + Provider.of(context, listen: false); + + final fromAddress = walletOptions.address.text; + final defaultWallet = myWalletProvider.getDefaultWallet(); + final walletsList = myWalletProvider.listWallets.toList(); + late WalletData selectedWallet; + + if (fromAddress == defaultWallet.address) { + selectedWallet = + walletsList[fromAddress == walletsList[0].address ? 1 : 0]; + } else { + selectedWallet = defaultWallet; + } + bool canValidate = false; + String validationStatus = ''; + + final mdStyle = MarkdownStyleSheet( + p: const TextStyle(fontSize: 18, color: Colors.black, letterSpacing: 0.3), + textAlign: WrapAlignment.center, + ); + + if (walletsList.length < 2) { + return Column( + children: [ + const SizedBox(height: 80), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: const [ + Text( + 'Vous devez avoir au moins 2 portefeuilles\npour effecter cette opération', + style: TextStyle(fontSize: 20), + ) + ], + ) + ], + ); + } + + return Scaffold( + backgroundColor: backgroundColor, + appBar: AppBar( + toolbarHeight: 60 * ratio, + title: SizedBox( + height: 22, + child: Text('importOldAccount'.tr()), + )), + body: SafeArea( + child: Consumer(builder: (context, sub, _) { + return FutureBuilder( + future: sub.getBalanceAndIdtyStatus( + fromAddress, selectedWallet.address!), + builder: (BuildContext context, AsyncSnapshot status) { + if (status.data == null) { + return Column(children: [ + const SizedBox(height: 80), + Row(mainAxisAlignment: MainAxisAlignment.center, children: [ + SizedBox( + height: 35, + width: 35, + child: CircularProgressIndicator( + color: orangeC, + strokeWidth: 4, + ), + ), + ]), + ]); + } + + // log.d('statusData: ${status.data}'); + + final Map balance = status.data?[0] ?? {}; + final String idtyStatus = status.data?[1]; + final String myIdtyStatus = status.data?[2]; + final bool hasConsumer = status.data?[3] ?? false; + + // log.d('hasconsumer: $hasConsumer'); + + if (balance['transferableBalance'] != 0 && !hasConsumer) { + canValidate = true; + validationStatus = ''; + } else { + canValidate = false; + validationStatus = hasConsumer + ? 'youMustWaitBeforeCashoutThisAccount'.tr(args: ['X']) + : 'thisAccountIsEmpty'.tr(); + } + + if (idtyStatus != 'noid' && myIdtyStatus != 'noid') { + canValidate = false; + validationStatus = + 'youCannotMigrateIdentityToExistingIdentity'.tr(); + } + + log.d( + 'tatatata: ${sub.g1V1NewAddress}, ${selectedWallet.address!}, $balance, $idtyStatus, $myIdtyStatus'); + + final walletsList = myWalletProvider.listWallets.toList(); + + walletsList + .removeWhere((element) => element.address == fromAddress); + // walletsList.add(WalletData(address: 'custom', name: 'custom')); + + return Column(children: [ + Row(children: const []), + const SizedBox(height: 20), + + SizedBox( + width: 350, + child: MarkdownBody( + data: 'areYouSureMigrateIdentity'.tr(args: [ + duniterIndexer.walletNameIndexer[fromAddress] ?? + '???', + '${balance['transferableBalance']} $currencyName' + ]), + styleSheet: mdStyle), + ), + // Text( + // 'areYouSureMigrateIdentity'.tr(args: [ + // duniterIndexer + // .walletNameIndexer[fromAddress]!, + // '$balance $currencyName' + // ]), + // textAlign: TextAlign.center, + // ), + const SizedBox(height: 20), + Text( + sub.g1V1NewAddress, + style: const TextStyle( + fontSize: 14.0, + color: Colors.black, + fontWeight: FontWeight.bold, + fontFamily: 'Monospace'), + ), + const SizedBox(height: 30), + Text('selectDestWallet'.tr()), + const SizedBox(height: 5), + DropdownButtonHideUnderline( + child: DropdownButton( + // alignment: AlignmentDirectional.topStart, + value: selectedWallet, + icon: const Icon(Icons.keyboard_arrow_down), + items: walletsList.map((wallet) { + return DropdownMenuItem( + value: wallet, + child: Text( + wallet.name!, + style: const TextStyle(fontSize: 18), + ), + ); + }).toList(), + onChanged: (WalletData? newSelectedWallet) { + selectedWallet = newSelectedWallet!; + sub.reload(); + }, + ), + ), + const SizedBox(height: 30), + SizedBox( + width: 380 * ratio, + height: 60 * ratio, + child: ElevatedButton( + style: ElevatedButton.styleFrom( + elevation: 4, + primary: orangeC, // background + onPrimary: Colors.white, // foreground + ), + onPressed: canValidate + ? () async { + log.d('GOOO'); + WalletData? defaultWallet = + myWalletProvider.getDefaultWallet(); + + String? pin; + if (myWalletProvider.pinCode == '') { + pin = await Navigator.push( + context, + MaterialPageRoute( + builder: (homeContext) { + return UnlockingWallet( + wallet: defaultWallet); + }, + ), + ); + } + + sub.migrateIdentity( + fromAddress: fromAddress, + destAddress: selectedWallet.address!, + fromPassword: pin ?? myWalletProvider.pinCode, + destPassword: pin ?? myWalletProvider.pinCode, + withBalance: true, + fromBalance: balance); + Navigator.push( + context, + MaterialPageRoute(builder: (context) { + return TransactionInProgress( + transType: 'identityMigration', + fromAddress: getShortPubkey(fromAddress), + toAddress: getShortPubkey( + selectedWallet.address!)); + }), + ); + } + : null, + child: Text( + 'migrateIdentity'.tr(), + style: TextStyle( + fontSize: 23 * ratio, fontWeight: FontWeight.w600), + ), + ), + ), + const SizedBox(height: 10), + Text( + validationStatus, + textAlign: TextAlign.center, + style: TextStyle(fontSize: 15, color: Colors.grey[600]), + ) + ]); + }); + }), + ), + ); + } +} diff --git a/lib/screens/myWallets/restore_chest.dart b/lib/screens/myWallets/restore_chest.dart index ddd414b..06540a5 100644 --- a/lib/screens/myWallets/restore_chest.dart +++ b/lib/screens/myWallets/restore_chest.dart @@ -49,110 +49,114 @@ class RestoreChest extends StatelessWidget { child: Text('restoreAChest'.tr()), )), body: SafeArea( - child: Column(children: [ - SizedBox(height: isTall ? 30 : 15), - bubbleSpeak('toRestoreEnterMnemonic'.tr()), - SizedBox(height: isTall ? 30 : 15), + child: Stack(children: [ Column(children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - arrayCell(context, genW.cellController0), - arrayCell(context, genW.cellController1), - arrayCell(context, genW.cellController2), - arrayCell(context, genW.cellController3), - ]), - const SizedBox(height: 15), - Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - arrayCell(context, genW.cellController4), - arrayCell(context, genW.cellController5), - arrayCell(context, genW.cellController6), - arrayCell(context, genW.cellController7), - ]), - const SizedBox(height: 15), - Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - arrayCell(context, genW.cellController8), - arrayCell(context, genW.cellController9), - arrayCell(context, genW.cellController10), - arrayCell(context, genW.cellController11), - ]), - ]), - // const Spacer(), - if (genW.isSentenceComplete(context)) - Expanded( - child: Align( - alignment: Alignment.center, - child: SizedBox( - width: 410, - height: 70, - child: ElevatedButton( - style: ElevatedButton.styleFrom( - elevation: 4, - primary: orangeC, // background - onPrimary: Colors.white, // foreground - ), - onPressed: () async { - if (await sub.isMnemonicValid(genW.generatedMnemonic!)) { - genW.resetImportView(); - await Navigator.push( - context, - FaderTransition( - page: skipIntro - ? const OnboardingStepNine( - scanDerivation: true) - : const OnboardingStepSeven( - scanDerivation: true), - isFast: true), - ); - } else { - await badMnemonicPopup(context); - } - }, - child: Text( - 'restoreThisChest'.tr(), - style: const TextStyle( - fontSize: 24, fontWeight: FontWeight.w600), - ), - ), - ), - // SizedBox(height: isTall ? 80 : 80), - )) - else - Column(children: [ - const SizedBox(height: 20), - SizedBox( - width: 190, - height: 60, - child: ElevatedButton( + SizedBox(height: isTall ? 30 : 15), + bubbleSpeak('toRestoreEnterMnemonic'.tr()), + SizedBox(height: isTall ? 30 : 15), + Column(children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + arrayCell(context, genW.cellController0), + arrayCell(context, genW.cellController1), + arrayCell(context, genW.cellController2), + arrayCell(context, genW.cellController3), + ]), + const SizedBox(height: 15), + Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + arrayCell(context, genW.cellController4), + arrayCell(context, genW.cellController5), + arrayCell(context, genW.cellController6), + arrayCell(context, genW.cellController7), + ]), + const SizedBox(height: 15), + Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + arrayCell(context, genW.cellController8), + arrayCell(context, genW.cellController9), + arrayCell(context, genW.cellController10), + arrayCell(context, genW.cellController11), + ]), + ]), + // const Spacer(), + if (genW.isSentenceComplete(context)) + Expanded( + child: Align( + alignment: Alignment.center, + child: SizedBox( + width: 410, + height: 70, + child: ElevatedButton( style: ElevatedButton.styleFrom( elevation: 4, - primary: yellowC, // background - onPrimary: Colors.black, // foreground + primary: orangeC, // background + onPrimary: Colors.white, // foreground ), - onPressed: () { - genW.pasteMnemonic(context); + onPressed: () async { + if (await sub + .isMnemonicValid(genW.generatedMnemonic!)) { + genW.resetImportView(); + await Navigator.push( + context, + FaderTransition( + page: skipIntro + ? const OnboardingStepNine( + scanDerivation: true) + : const OnboardingStepSeven( + scanDerivation: true), + isFast: true), + ); + } else { + await badMnemonicPopup(context); + } }, - child: Row( - children: [ - const Icon( - Icons.content_paste_go, - size: 25, - ), - const SizedBox(width: 10), - Text( - 'pasteFromClipboard'.tr(), - textAlign: TextAlign.center, - style: const TextStyle( - fontSize: 17, fontWeight: FontWeight.w400), - ), - ], - )), - ) - ]) + child: Text( + 'restoreThisChest'.tr(), + style: const TextStyle( + fontSize: 24, fontWeight: FontWeight.w600), + ), + ), + ), + // SizedBox(height: isTall ? 80 : 80), + )) + else + Column(children: [ + const SizedBox(height: 20), + SizedBox( + width: 190, + height: 60, + child: ElevatedButton( + style: ElevatedButton.styleFrom( + elevation: 4, + primary: yellowC, // background + onPrimary: Colors.black, // foreground + ), + onPressed: () { + genW.pasteMnemonic(context); + }, + child: Row( + children: [ + const Icon( + Icons.content_paste_go, + size: 25, + ), + const SizedBox(width: 10), + Text( + 'pasteFromClipboard'.tr(), + textAlign: TextAlign.center, + style: const TextStyle( + fontSize: 17, fontWeight: FontWeight.w400), + ), + ], + )), + ) + ]) + ]), + CommonElements().offlineInfo(context), ]), ), ), diff --git a/lib/screens/myWallets/wallets_home.dart b/lib/screens/myWallets/wallets_home.dart index 6fcd74f..19a324c 100644 --- a/lib/screens/myWallets/wallets_home.dart +++ b/lib/screens/myWallets/wallets_home.dart @@ -14,6 +14,7 @@ import 'package:gecko/providers/wallet_options.dart'; import 'package:gecko/screens/common_elements.dart'; import 'package:gecko/screens/myWallets/chest_options.dart'; import 'package:gecko/screens/myWallets/choose_chest.dart'; +import 'package:gecko/screens/myWallets/import_g1_v1.dart'; import 'package:gecko/screens/myWallets/unlocking_wallet.dart'; import 'package:gecko/screens/myWallets/wallet_options.dart'; import 'package:provider/provider.dart'; @@ -64,12 +65,10 @@ class WalletsHome extends StatelessWidget { ), bottomNavigationBar: homeProvider.bottomAppBar(context), body: SafeArea( - child: Stack( - children: [ - myWalletsTiles(context, currentChestNumber), - CommonElements().offlineInfo(context), - ], - ), + child: Stack(children: [ + myWalletsTiles(context, currentChestNumber), + CommonElements().offlineInfo(context), + ]), ), ), ); @@ -80,7 +79,7 @@ class WalletsHome extends StatelessWidget { return Column(children: [ const SizedBox(height: 50), SizedBox( - height: 90, + height: 80, width: 420, child: ElevatedButton.icon( icon: Image.asset( @@ -108,6 +107,28 @@ class WalletsHome extends StatelessWidget { ), )), const SizedBox(height: 30), + InkWell( + key: const Key('importG1v1'), + onTap: () { + Navigator.push( + context, + MaterialPageRoute(builder: (context) { + return const ImportG1v1(); + }), + ); + }, + child: SizedBox( + width: 400, + height: 60, + child: Center( + child: Text('importG1v1'.tr(), + style: TextStyle( + fontSize: 22, + color: Colors.blue[900], + fontWeight: FontWeight.w500))), + ), + ), + const SizedBox(height: 5), InkWell( key: const Key('changeChest'), onTap: () { @@ -120,7 +141,7 @@ class WalletsHome extends StatelessWidget { }, child: SizedBox( width: 400, - height: 50, + height: 60, child: Center( child: Text('changeChest'.tr(), style: TextStyle( @@ -168,7 +189,6 @@ class WalletsHome extends StatelessWidget { return CustomScrollView(slivers: [ const SliverToBoxAdapter(child: SizedBox(height: 20)), - SliverGrid.count( key: const Key('listWallets'), crossAxisCount: nTule, diff --git a/lib/screens/onBoarding/1.dart b/lib/screens/onBoarding/1.dart index 2550f47..a13257d 100644 --- a/lib/screens/onBoarding/1.dart +++ b/lib/screens/onBoarding/1.dart @@ -27,15 +27,18 @@ class OnboardingStepOne extends StatelessWidget { ), extendBodyBehindAppBar: true, body: SafeArea( - child: common.infoIntro( - context, - 'geckoGenerateYourWalletFromMnemonic'.tr(), - 'fabrication-de-portefeuille.png', - '>', - const OnboardingStepTwo(), - 0, - isMd: true, - ), + child: Stack(children: [ + common.infoIntro( + context, + 'geckoGenerateYourWalletFromMnemonic'.tr(), + 'fabrication-de-portefeuille.png', + '>', + const OnboardingStepTwo(), + 0, + isMd: true, + ), + CommonElements().offlineInfo(context), + ]), ), ); } diff --git a/lib/screens/onBoarding/10.dart b/lib/screens/onBoarding/10.dart index ad58bac..2680d33 100644 --- a/lib/screens/onBoarding/10.dart +++ b/lib/screens/onBoarding/10.dart @@ -49,79 +49,82 @@ class OnboardingStepTen extends StatelessWidget { ), extendBodyBehindAppBar: true, body: SafeArea( - child: Column(children: [ - SizedBox(height: isTall ? 40 : 20), - common.buildProgressBar(9), - SizedBox(height: isTall ? 40 : 20), - common.buildText("geckoWillCheckPassword".tr()), - SizedBox(height: isTall ? 80 : 20), - Visibility( - visible: generateWalletProvider.scanedValidWalletNumber != -1, - child: Padding( - padding: const EdgeInsets.only(bottom: 15), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text("derivationsScanProgress".tr(args: [ - '${generateWalletProvider.scanedWalletNumber}', - '${generateWalletProvider.numberScan + 1}' - ])), - const SizedBox(width: 10), - SizedBox( - height: 22, - width: 22, - child: CircularProgressIndicator( - color: orangeC, - strokeWidth: 3, + child: Stack(children: [ + Column(children: [ + SizedBox(height: isTall ? 40 : 20), + common.buildProgressBar(9), + SizedBox(height: isTall ? 40 : 20), + common.buildText("geckoWillCheckPassword".tr()), + SizedBox(height: isTall ? 80 : 20), + Visibility( + visible: generateWalletProvider.scanedValidWalletNumber != -1, + child: Padding( + padding: const EdgeInsets.only(bottom: 15), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text("derivationsScanProgress".tr(args: [ + '${generateWalletProvider.scanedWalletNumber}', + '${generateWalletProvider.numberScan + 1}' + ])), + const SizedBox(width: 10), + SizedBox( + height: 22, + width: 22, + child: CircularProgressIndicator( + color: orangeC, + strokeWidth: 3, + ), ), - ), - ], + ], + ), ), ), - ), - Consumer(builder: (context, sub, _) { - return sub.nodeConnected - ? pinForm(context, walletOptions, pinLenght, 1, 2) - : Row( - mainAxisAlignment: MainAxisAlignment.center, - children: const [ - Text( - 'Vous devez vous connecter à internet\npour valider votre coffre', - style: TextStyle( - fontSize: 20, - color: Colors.redAccent, - fontWeight: FontWeight.w500, + Consumer(builder: (context, sub, _) { + return sub.nodeConnected + ? pinForm(context, walletOptions, pinLenght, 1, 2) + : Row( + mainAxisAlignment: MainAxisAlignment.center, + children: const [ + Text( + 'Vous devez vous connecter à internet\npour valider votre coffre', + style: TextStyle( + fontSize: 20, + color: Colors.redAccent, + fontWeight: FontWeight.w500, + ), + textAlign: TextAlign.center, ), - textAlign: TextAlign.center, + ]); + }), + Consumer(builder: (context, sub, _) { + return sub.nodeConnected + ? InkWell( + onTap: () { + walletOptions.changePinCacheChoice(); + }, + child: Row(children: [ + const SizedBox(height: 30), + const Spacer(), + Icon( + configBox.get('isCacheChecked') ?? false + ? Icons.check_box + : Icons.check_box_outline_blank, + color: orangeC, ), - ]); - }), - Consumer(builder: (context, sub, _) { - return sub.nodeConnected - ? InkWell( - onTap: () { - walletOptions.changePinCacheChoice(); - }, - child: Row(children: [ - const SizedBox(height: 30), - const Spacer(), - Icon( - configBox.get('isCacheChecked') ?? false - ? Icons.check_box - : Icons.check_box_outline_blank, - color: orangeC, - ), - const SizedBox(width: 8), - Text( - 'rememberPassword'.tr(), - style: - TextStyle(fontSize: 16, color: Colors.grey[700]), - ), - const Spacer() - ])) - : const Text(''); - }), - const SizedBox(height: 10), + const SizedBox(width: 8), + Text( + 'rememberPassword'.tr(), + style: TextStyle( + fontSize: 16, color: Colors.grey[700]), + ), + const Spacer() + ])) + : const Text(''); + }), + const SizedBox(height: 10), + ]), + CommonElements().offlineInfo(context), ]), )); } diff --git a/lib/screens/onBoarding/2.dart b/lib/screens/onBoarding/2.dart index ee16d16..82e21c3 100644 --- a/lib/screens/onBoarding/2.dart +++ b/lib/screens/onBoarding/2.dart @@ -29,13 +29,16 @@ class OnboardingStepTwo extends StatelessWidget { ), extendBodyBehindAppBar: true, body: SafeArea( - child: common.infoIntro( - context, - 'keepThisMnemonicSecure'.tr(), - 'fabrication-de-portefeuille-impossible-sans-phrase.png', - '>', - const OnboardingStepThree(), - 1), + child: Stack(children: [ + common.infoIntro( + context, + 'keepThisMnemonicSecure'.tr(), + 'fabrication-de-portefeuille-impossible-sans-phrase.png', + '>', + const OnboardingStepThree(), + 1), + CommonElements().offlineInfo(context), + ]), ), ); } diff --git a/lib/screens/onBoarding/3.dart b/lib/screens/onBoarding/3.dart index 8a261d9..0099402 100644 --- a/lib/screens/onBoarding/3.dart +++ b/lib/screens/onBoarding/3.dart @@ -29,8 +29,11 @@ class OnboardingStepThree extends StatelessWidget { ), extendBodyBehindAppBar: true, body: SafeArea( - child: common.infoIntro(context, 'warningForgotPassword'.tr(), - 'forgot_password.png'.tr(), '>', const OnboardingStepFor(), 2), + child: Stack(children: [ + common.infoIntro(context, 'warningForgotPassword'.tr(), + 'forgot_password.png'.tr(), '>', const OnboardingStepFor(), 2), + CommonElements().offlineInfo(context), + ]), ), ); } diff --git a/lib/screens/onBoarding/4.dart b/lib/screens/onBoarding/4.dart index 946f3db..d2ce061 100644 --- a/lib/screens/onBoarding/4.dart +++ b/lib/screens/onBoarding/4.dart @@ -29,14 +29,17 @@ class OnboardingStepFor extends StatelessWidget { ), extendBodyBehindAppBar: true, body: SafeArea( - child: common.infoIntro( - context, - 'itsTimeToUseAPenAndPaper'.tr(), - 'gecko_also_can_forget.png'.tr(), - '>', - const OnboardingStepFive(), - 3, - isMd: true), + child: Stack(children: [ + common.infoIntro( + context, + 'itsTimeToUseAPenAndPaper'.tr(), + 'gecko_also_can_forget.png'.tr(), + '>', + const OnboardingStepFive(), + 3, + isMd: true), + CommonElements().offlineInfo(context), + ]), ), ); } diff --git a/lib/screens/onBoarding/5.dart b/lib/screens/onBoarding/5.dart index a2234f4..6b9426f 100644 --- a/lib/screens/onBoarding/5.dart +++ b/lib/screens/onBoarding/5.dart @@ -48,57 +48,62 @@ class _ChooseChestState extends State { ), extendBodyBehindAppBar: true, body: SafeArea( - child: Column(children: [ - SizedBox(height: isTall ? 40 : 20), - common.buildProgressBar(4), - SizedBox(height: isTall ? 40 : 20), - common.buildText('geckoGeneratedYourMnemonicKeepItSecret'.tr()), - SizedBox(height: 35 * ratio), - sentanceArray(context), - SizedBox(height: 17 * ratio), - GestureDetector( - onTap: () { - Navigator.push( - context, - MaterialPageRoute(builder: (context) { - return PrintWallet(generateWalletProvider.generatedMnemonic); - }), - ); - }, - child: Image.asset( - 'assets/printer.png', - height: 42 * ratio, - ), - ), - const SizedBox(height: 40), - Expanded( - child: Align( - alignment: Alignment.bottomCenter, - child: SizedBox( - width: 380 * ratio, - height: 60 * ratio, - child: ElevatedButton( - key: const Key('generateMnemonic'), - style: ElevatedButton.styleFrom( - elevation: 4, - primary: const Color(0xffFFD58D), - onPrimary: Colors.black, // foreground - ), - onPressed: () { - // _generateWalletProvider.reloadBuild(); - setState(() {}); - }, - child: Text("chooseAnotherMnemonic".tr(), - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 22 * ratio, - fontWeight: FontWeight.w600))), + child: Stack(children: [ + Column(children: [ + SizedBox(height: isTall ? 40 : 20), + common.buildProgressBar(4), + SizedBox(height: isTall ? 40 : 20), + common.buildText('geckoGeneratedYourMnemonicKeepItSecret'.tr()), + SizedBox(height: 35 * ratio), + sentanceArray(context), + SizedBox(height: 17 * ratio), + GestureDetector( + onTap: () { + Navigator.push( + context, + MaterialPageRoute(builder: (context) { + return PrintWallet( + generateWalletProvider.generatedMnemonic); + }), + ); + }, + child: Image.asset( + 'assets/printer.png', + height: 42 * ratio, ), ), - ), - SizedBox(height: 22 * ratio), - nextButton(context, "iNotedMyMnemonic".tr(), false, widget.skipIntro), - SizedBox(height: 35 * ratio), + const SizedBox(height: 40), + Expanded( + child: Align( + alignment: Alignment.bottomCenter, + child: SizedBox( + width: 380 * ratio, + height: 60 * ratio, + child: ElevatedButton( + key: const Key('generateMnemonic'), + style: ElevatedButton.styleFrom( + elevation: 4, + primary: const Color(0xffFFD58D), + onPrimary: Colors.black, // foreground + ), + onPressed: () { + // _generateWalletProvider.reloadBuild(); + setState(() {}); + }, + child: Text("chooseAnotherMnemonic".tr(), + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 22 * ratio, + fontWeight: FontWeight.w600))), + ), + ), + ), + SizedBox(height: 22 * ratio), + nextButton( + context, "iNotedMyMnemonic".tr(), false, widget.skipIntro), + SizedBox(height: 35 * ratio), + ]), + CommonElements().offlineInfo(context), ]), ), ); diff --git a/lib/screens/onBoarding/6.dart b/lib/screens/onBoarding/6.dart index 69922c3..5a58006 100644 --- a/lib/screens/onBoarding/6.dart +++ b/lib/screens/onBoarding/6.dart @@ -51,88 +51,92 @@ class OnboardingStepSix extends StatelessWidget { ), ), body: SafeArea( - child: Align( - alignment: Alignment.topCenter, - child: Column(children: [ - SizedBox(height: isTall ? 40 : 20), - common.buildProgressBar(5), - SizedBox(height: isTall ? 40 : 20), - common.buildText( - "didYouNoteMnemonicToBeSureTypeWord".tr( - args: [(generateWalletProvider.nbrWord + 1).toString()]), - 20, - true), - SizedBox(height: isTall ? 70 : 20), - Text('${generateWalletProvider.nbrWord + 1}', - key: const Key('askedWord'), - style: TextStyle( - fontSize: isTall ? 17 : 15, - color: orangeC, - fontWeight: FontWeight.w400)), - const SizedBox(height: 10), - Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(7), - border: Border.all( - color: Colors.grey[600]!, - width: 3, - )), - width: 430, - child: TextFormField( - key: const Key('inputWord'), - autofocus: true, - enabled: !generateWalletProvider.isAskedWordValid, - controller: wordController, - textInputAction: TextInputAction.next, - onChanged: (value) { - generateWalletProvider.checkAskedWord( - value, _mnemonicController.text); - }, - maxLines: 1, - textAlign: TextAlign.center, - decoration: InputDecoration( - labelStyle: TextStyle( - fontSize: 22.0, - color: Colors.grey[500], - fontWeight: FontWeight.w500), - labelText: generateWalletProvider.isAskedWordValid - ? "itsTheGoodWord".tr() - : "${generateWalletProvider.nbrWordAlpha} ${"nthMnemonicWord".tr()}", - fillColor: const Color(0xffeeeedd), - filled: true, - contentPadding: const EdgeInsets.all(12), - ), - style: TextStyle( - fontSize: 40.0, - color: generateWalletProvider.askedWordColor, - fontWeight: FontWeight.w500))), - Visibility( - visible: generateWalletProvider.isAskedWordValid, - child: Expanded( - child: Align( - alignment: Alignment.bottomCenter, - child: nextButton( - context, - 'continue'.tr(), - skipIntro - ? const OnboardingStepNine() - : const OnboardingStepSeven(), - false), + child: Stack(children: [ + Align( + alignment: Alignment.topCenter, + child: Column(children: [ + SizedBox(height: isTall ? 40 : 20), + common.buildProgressBar(5), + SizedBox(height: isTall ? 40 : 20), + common.buildText( + "didYouNoteMnemonicToBeSureTypeWord".tr(args: [ + (generateWalletProvider.nbrWord + 1).toString() + ]), + 20, + true), + SizedBox(height: isTall ? 70 : 20), + Text('${generateWalletProvider.nbrWord + 1}', + key: const Key('askedWord'), + style: TextStyle( + fontSize: isTall ? 17 : 15, + color: orangeC, + fontWeight: FontWeight.w400)), + const SizedBox(height: 10), + Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(7), + border: Border.all( + color: Colors.grey[600]!, + width: 3, + )), + width: 430, + child: TextFormField( + key: const Key('inputWord'), + autofocus: true, + enabled: !generateWalletProvider.isAskedWordValid, + controller: wordController, + textInputAction: TextInputAction.next, + onChanged: (value) { + generateWalletProvider.checkAskedWord( + value, _mnemonicController.text); + }, + maxLines: 1, + textAlign: TextAlign.center, + decoration: InputDecoration( + labelStyle: TextStyle( + fontSize: 22.0, + color: Colors.grey[500], + fontWeight: FontWeight.w500), + labelText: generateWalletProvider.isAskedWordValid + ? "itsTheGoodWord".tr() + : "${generateWalletProvider.nbrWordAlpha} ${"nthMnemonicWord".tr()}", + fillColor: const Color(0xffeeeedd), + filled: true, + contentPadding: const EdgeInsets.all(12), + ), + style: TextStyle( + fontSize: 40.0, + color: generateWalletProvider.askedWordColor, + fontWeight: FontWeight.w500))), + Visibility( + visible: generateWalletProvider.isAskedWordValid, + child: Expanded( + child: Align( + alignment: Alignment.bottomCenter, + child: nextButton( + context, + 'continue'.tr(), + skipIntro + ? const OnboardingStepNine() + : const OnboardingStepSeven(), + false), + ), ), ), - ), - // Visibility( - // visible: !_generateWalletProvider.isAskedWordValid, - // child: const Expanded( - // child: Align( - // alignment: Alignment.bottomCenter, - // child: Text(''), - // ), - // ), - // ), - SizedBox(height: 35 * ratio), - ]), - ), + // Visibility( + // visible: !_generateWalletProvider.isAskedWordValid, + // child: const Expanded( + // child: Align( + // alignment: Alignment.bottomCenter, + // child: Text(''), + // ), + // ), + // ), + SizedBox(height: 35 * ratio), + ]), + ), + CommonElements().offlineInfo(context), + ]), ), ), ); diff --git a/lib/screens/onBoarding/7.dart b/lib/screens/onBoarding/7.dart index b266009..4313930 100644 --- a/lib/screens/onBoarding/7.dart +++ b/lib/screens/onBoarding/7.dart @@ -29,14 +29,17 @@ class OnboardingStepSeven extends StatelessWidget { ), extendBodyBehindAppBar: true, body: SafeArea( - child: common.infoIntro( - context, - 'geckoWillGenerateAPassword'.tr(), - 'coffre-fort-code-secret-dans-telephone.png', - '>', - OnboardingStepEight(scanDerivation: scanDerivation), - 6, - boxHeight: 400), + child: Stack(children: [ + common.infoIntro( + context, + 'geckoWillGenerateAPassword'.tr(), + 'coffre-fort-code-secret-dans-telephone.png', + '>', + OnboardingStepEight(scanDerivation: scanDerivation), + 6, + boxHeight: 400), + CommonElements().offlineInfo(context), + ]), ), ); } diff --git a/lib/screens/onBoarding/8.dart b/lib/screens/onBoarding/8.dart index 2172e2d..905535a 100644 --- a/lib/screens/onBoarding/8.dart +++ b/lib/screens/onBoarding/8.dart @@ -29,14 +29,17 @@ class OnboardingStepEight extends StatelessWidget { ), extendBodyBehindAppBar: true, body: SafeArea( - child: common.infoIntro( - context, - 'thisPasswordProtectsYourWalletsInASecureChest'.tr(), - 'coffre-fort-protege-les-portefeuilles.png', - '>', - OnboardingStepNine(scanDerivation: scanDerivation), - 7, - isMd: true), + child: Stack(children: [ + common.infoIntro( + context, + 'thisPasswordProtectsYourWalletsInASecureChest'.tr(), + 'coffre-fort-protege-les-portefeuilles.png', + '>', + OnboardingStepNine(scanDerivation: scanDerivation), + 7, + isMd: true), + CommonElements().offlineInfo(context), + ]), ), ); } diff --git a/lib/screens/onBoarding/9.dart b/lib/screens/onBoarding/9.dart index 0cf8ecf..ed29fce 100644 --- a/lib/screens/onBoarding/9.dart +++ b/lib/screens/onBoarding/9.dart @@ -41,61 +41,65 @@ class OnboardingStepNine extends StatelessWidget { ), extendBodyBehindAppBar: true, body: SafeArea( - child: Column(children: [ - SizedBox(height: isTall ? 40 : 20), - common.buildProgressBar(8), - SizedBox(height: isTall ? 40 : 20), - common.buildText("hereIsThePasswordKeepIt".tr(), 20, true), - const SizedBox(height: 100), - Stack( - alignment: Alignment.centerRight, - children: [ - TextField( - key: const Key('generatedPin'), - enabled: false, - controller: generateWalletProvider.pin, - maxLines: 1, - textAlign: TextAlign.center, - decoration: const InputDecoration(), - style: const TextStyle( - letterSpacing: 5, - fontSize: 35.0, - color: Colors.black, - fontWeight: FontWeight.bold)), - IconButton( - icon: const Icon(Icons.replay), - color: orangeC, - onPressed: () { - generateWalletProvider.changePinCode(reload: true); - }, - ), - ], - ), - Expanded( - child: Align( - alignment: Alignment.bottomCenter, - child: SizedBox( - width: 380 * ratio, - height: 60 * ratio, - child: ElevatedButton( - key: const Key('changeSecretCode'), - style: ElevatedButton.styleFrom( - elevation: 4, - primary: const Color(0xffFFD58D), - onPrimary: Colors.black, // foreground - ), - onPressed: () { - generateWalletProvider.changePinCode(reload: true); - }, - child: Text("chooseAnotherPassword".tr(), - style: TextStyle( - fontSize: 22 * ratio, - fontWeight: FontWeight.w600))), - ))), - SizedBox(height: 22 * ratio), - common.nextButton(context, "iNotedMyPassword".tr(), - OnboardingStepTen(scanDerivation: scanDerivation), false), - SizedBox(height: 35 * ratio), + child: Stack(children: [ + Column(children: [ + SizedBox(height: isTall ? 40 : 20), + common.buildProgressBar(8), + SizedBox(height: isTall ? 40 : 20), + common.buildText("hereIsThePasswordKeepIt".tr(), 20, true), + const SizedBox(height: 100), + Stack( + alignment: Alignment.centerRight, + children: [ + TextField( + key: const Key('generatedPin'), + enabled: false, + controller: generateWalletProvider.pin, + maxLines: 1, + textAlign: TextAlign.center, + decoration: const InputDecoration(), + style: const TextStyle( + letterSpacing: 5, + fontSize: 35.0, + color: Colors.black, + fontWeight: FontWeight.bold)), + IconButton( + icon: const Icon(Icons.replay), + color: orangeC, + onPressed: () { + generateWalletProvider.changePinCode(reload: true); + }, + ), + ], + ), + Expanded( + child: Align( + alignment: Alignment.bottomCenter, + child: SizedBox( + width: 380 * ratio, + height: 60 * ratio, + child: ElevatedButton( + key: const Key('changeSecretCode'), + style: ElevatedButton.styleFrom( + elevation: 4, + primary: const Color(0xffFFD58D), + onPrimary: Colors.black, // foreground + ), + onPressed: () { + generateWalletProvider.changePinCode( + reload: true); + }, + child: Text("chooseAnotherPassword".tr(), + style: TextStyle( + fontSize: 22 * ratio, + fontWeight: FontWeight.w600))), + ))), + SizedBox(height: 22 * ratio), + common.nextButton(context, "iNotedMyPassword".tr(), + OnboardingStepTen(scanDerivation: scanDerivation), false), + SizedBox(height: 35 * ratio), + ]), + CommonElements().offlineInfo(context), ]), )); } diff --git a/lib/screens/transaction_in_progress.dart b/lib/screens/transaction_in_progress.dart index 548ba06..dafd381 100644 --- a/lib/screens/transaction_in_progress.dart +++ b/lib/screens/transaction_in_progress.dart @@ -11,9 +11,12 @@ import 'package:provider/provider.dart'; // ignore: must_be_immutable class TransactionInProgress extends StatelessWidget { - const TransactionInProgress({Key? key, this.transType = 'pay'}) + const TransactionInProgress( + {Key? key, this.transType = 'pay', this.fromAddress, this.toAddress}) : super(key: key); final String transType; + final String? fromAddress; + final String? toAddress; @override Widget build(BuildContext context) { @@ -32,8 +35,8 @@ class TransactionInProgress extends StatelessWidget { log.d(walletViewProvider.address!); - final from = myWalletProvider.getDefaultWallet().name!; - final to = getShortPubkey(walletViewProvider.address!); + final from = fromAddress ?? myWalletProvider.getDefaultWallet().name!; + final to = toAddress ?? getShortPubkey(walletViewProvider.address!); final amount = walletViewProvider.payAmount.text; String actionName = ''; @@ -58,6 +61,11 @@ class TransactionInProgress extends StatelessWidget { actionName = "revokeAdhesion".tr(); } break; + case 'identityMigration': + { + actionName = "identityMigration".tr(); + } + break; default: { actionName = 'strangeTransaction'.tr(); @@ -88,6 +96,8 @@ class TransactionInProgress extends StatelessWidget { if (result.contains('blockHash: ')) { isValid = true; resultText = 'extrinsicValidated'.tr(args: [actionName]); + log.i( + 'g1migration Bloc of last transaction: ${sub.blocNumber} --- $result'); } else { isValid = false; resultText = "${"anErrorOccured".tr()}:\n"; @@ -146,7 +156,9 @@ class TransactionInProgress extends StatelessWidget { onWillPop: () { sub.transactionStatus = ''; Navigator.pop(context); - if (transType == 'pay') Navigator.pop(context); + if (transType == 'pay' || transType == 'identityMigration') { + Navigator.pop(context); + } return Future.value(true); }, child: Scaffold( @@ -259,7 +271,10 @@ class TransactionInProgress extends StatelessWidget { onPressed: () { Navigator.pop(context); sub.transactionStatus = ''; - if (transType == 'pay') Navigator.pop(context); + if (transType == 'pay' || + transType == 'identityMigration') { + Navigator.pop(context); + } }, child: Text( 'close'.tr(), diff --git a/lib/screens/wallet_view.dart b/lib/screens/wallet_view.dart index 1593780..bbe47d7 100644 --- a/lib/screens/wallet_view.dart +++ b/lib/screens/wallet_view.dart @@ -191,6 +191,8 @@ class WalletViewScreen extends StatelessWidget { } } + final toStatus = snapshot.data!['toStatus'] ?? 0; + return Visibility( visible: (snapshot.data != {}), child: Column(children: [ @@ -268,67 +270,14 @@ class WalletViewScreen extends StatelessWidget { fontSize: buttonFontSize, fontWeight: FontWeight.w500), ), - ]), - if (snapshot.data!['certDelay'] != null) - Column(children: [ - SizedBox( - height: buttonSize, - child: Padding( - padding: const EdgeInsets.only(bottom: 0), - child: Container( - foregroundDecoration: const BoxDecoration( - color: Colors.grey, - backgroundBlendMode: BlendMode.saturation, - ), - child: const Opacity( - opacity: 0.5, - child: Image( - image: AssetImage( - 'assets/gecko_certify.png')), - ), - ), - ), - ), - Text( - "mustWaitXBeforeCertify" - .tr(args: [duration.toString()]), - textAlign: TextAlign.center, - style: TextStyle( - fontSize: buttonFontSize - 4, - fontWeight: FontWeight.w400, - color: Colors.grey[600]), - ), - ]), - if (snapshot.data!['certRenewable'] != null && + ]) + else if (toStatus == 1) + waitToCert('mustConfirmHisIdentity', duration) + else if (snapshot.data!['certRenewable'] != null && duration != 'seconds'.tr(args: ['0'])) - Column(children: [ - SizedBox( - height: buttonSize, - child: Padding( - padding: const EdgeInsets.only(bottom: 0), - child: Container( - foregroundDecoration: const BoxDecoration( - color: Colors.grey, - backgroundBlendMode: BlendMode.saturation, - ), - child: const Opacity( - opacity: 0.5, - child: Image( - image: AssetImage( - 'assets/gecko_certify.png')), - ), - ), - ), - ), - Text( - "canRenewCertInX".tr(args: [duration.toString()]), - textAlign: TextAlign.center, - style: TextStyle( - fontSize: buttonFontSize - 4, - fontWeight: FontWeight.w400, - color: Colors.grey[600]), - ), - ]), + waitToCert('canRenewCertInX', duration) + else if (snapshot.data!['certDelay'] != null) + waitToCert('mustWaitXBeforeCertify', duration) ]), ); }, @@ -415,6 +364,35 @@ class WalletViewScreen extends StatelessWidget { )); } + Widget waitToCert(String status, String duration) { + return Column(children: [ + SizedBox( + height: buttonSize, + child: Padding( + padding: const EdgeInsets.only(bottom: 0), + child: Container( + foregroundDecoration: const BoxDecoration( + color: Colors.grey, + backgroundBlendMode: BlendMode.saturation, + ), + child: const Opacity( + opacity: 0.5, + child: Image(image: AssetImage('assets/gecko_certify.png')), + ), + ), + ), + ), + Text( + status.tr(args: [duration]), + textAlign: TextAlign.center, + style: TextStyle( + fontSize: buttonFontSize - 4, + fontWeight: FontWeight.w400, + color: Colors.grey[600]), + ), + ]); + } + void paymentPopup( BuildContext context, WalletsProfilesProvider walletViewProvider) { // WalletsProfilesProvider _walletViewProvider = diff --git a/pubspec.lock b/pubspec.lock index 017b92e..d404e72 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -557,7 +557,7 @@ packages: source: hosted version: "2.1.0" hex: - dependency: transitive + dependency: "direct main" description: name: hex url: "https://pub.dartlang.org" @@ -1052,7 +1052,7 @@ packages: source: hosted version: "2.1.2" pointycastle: - dependency: transitive + dependency: "direct main" description: name: pointycastle url: "https://pub.dartlang.org" @@ -1062,8 +1062,8 @@ packages: dependency: "direct main" description: path: "." - ref: gecko-old - resolved-ref: "87096351fb187614b6386343615db39a2f2b5b9b" + ref: gecko-unwrapbytes + resolved-ref: b019cc2a6e77b6989f94b1bef8259298410cd82a url: "https://github.com/poka-IT/sdk.git" source: git version: "0.4.8" diff --git a/pubspec.yaml b/pubspec.yaml index 7ae857b..2becac7 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -5,7 +5,7 @@ description: Pay with G1. # pub.dev using `pub publish`. This is preferred for private packages. publish_to: 'none' # Remove this line if you wish to publish to pub.dev -version: 0.0.9+21 +version: 0.0.10+26 environment: sdk: '>=2.12.0 <3.0.0' @@ -62,8 +62,11 @@ dependencies: package_info_plus: ^1.4.2 polkawallet_sdk: #^0.4.9 git: + # url: https://github.com/polkawallet-io/sdk.git + # ref: develop url: https://github.com/poka-IT/sdk.git - ref: gecko-old + # ref: gecko-old + ref: gecko-unwrapbytes dots_indicator: ^2.1.0 web_socket_channel: ^2.2.0 connectivity_plus: ^2.3.3 @@ -71,6 +74,8 @@ dependencies: easy_localization: ^3.0.1 flutter_markdown: ^0.6.10+2 dropdown_button2: ^1.6.3 + pointycastle: ^3.6.1 + hex: ^0.2.0 dev_dependencies: # flutter_launcher_icons: ^0.9.2