diff --git a/lib/app/shared/constants/parameters.dart b/lib/app/shared/constants/parameters.dart index cd3209bf5..dcc3b0808 100644 --- a/lib/app/shared/constants/parameters.dart +++ b/lib/app/shared/constants/parameters.dart @@ -31,7 +31,7 @@ class Parameters { static const web3RpcMainnetUrl = 'https://mainnet.infura.io/v3/'; static const POLYGON_MAIN_NETWORK = 'main'; - static const INFURA_URL = 'https://polygon-mainnet.infura.io/v3/'; + static const POLYGON_INFURA_URL = 'https://polygon-mainnet.infura.io/v3/'; static const INFURA_RDP_URL = 'wss://polygon-mainnet.infura.io/v3/'; static const ID_STATE_CONTRACT_ADDR = '0x624ce98D2d27b20b8f8d521723Df8fC4db71D79D'; diff --git a/lib/app/shared/constants/secure_storage_keys.dart b/lib/app/shared/constants/secure_storage_keys.dart index dfcc2d1a8..fc3a01279 100644 --- a/lib/app/shared/constants/secure_storage_keys.dart +++ b/lib/app/shared/constants/secure_storage_keys.dart @@ -37,6 +37,8 @@ class SecureStorageKeys { static const String ssiKey = 'ssi/key'; static const String p256PrivateKeyForWallet = 'p256PrivateKeyForWallet'; + static const String p256PrivateKeyToGetAndPresentVC = + 'p256PrivateKeyToGetAndPresentVC'; static const String cryptoAccount = 'cryptoAccount'; static const String cryptoAccounTrackingIndex = 'cryptoAccounTrackingIndex'; diff --git a/lib/app/shared/enum/type/blockchain_type.dart b/lib/app/shared/enum/type/blockchain_type.dart index 05ab76c78..333f1f54f 100644 --- a/lib/app/shared/enum/type/blockchain_type.dart +++ b/lib/app/shared/enum/type/blockchain_type.dart @@ -66,7 +66,12 @@ extension BlockchainTypeX on BlockchainType { String name = ''; switch (this) { case BlockchainType.tezos: - throw Exception(); + throw ResponseMessage( + data: { + 'error': 'invalid_request', + 'error_description': 'Chain is not supported for tezos.', + }, + ); case BlockchainType.ethereum: name = '1'; @@ -87,7 +92,12 @@ extension BlockchainTypeX on BlockchainType { int get chainId { switch (this) { case BlockchainType.tezos: - throw Exception(); + throw ResponseMessage( + data: { + 'error': 'invalid_request', + 'error_description': 'Chain is not supported for tezos.', + }, + ); case BlockchainType.ethereum: return 1; diff --git a/lib/app/shared/enum/type/credential_subject_type/credential_subject_type_extension.dart b/lib/app/shared/enum/type/credential_subject_type/credential_subject_type_extension.dart index 95cb8462c..abdce2a98 100644 --- a/lib/app/shared/enum/type/credential_subject_type/credential_subject_type_extension.dart +++ b/lib/app/shared/enum/type/credential_subject_type/credential_subject_type_extension.dart @@ -395,7 +395,12 @@ extension CredentialSubjectTypeExtension on CredentialSubjectType { } else if (this == CredentialSubjectType.ageRange) { return Urls.ageRangeAIValidationUrl; } else { - throw Exception(); + throw ResponseMessage( + data: { + 'error': 'invalid_request', + 'error_description': 'Url is not specified for $name.', + }, + ); } } @@ -672,6 +677,7 @@ extension CredentialSubjectTypeExtension on CredentialSubjectType { VCFormatType.ldpVc, VCFormatType.jwtVcJson, VCFormatType.vcSdJWT, + VCFormatType.jwtVc, ]; case CredentialSubjectType.identityCredential: @@ -894,6 +900,8 @@ extension CredentialSubjectTypeExtension on CredentialSubjectType { if (vcFormatType == VCFormatType.vcSdJWT) { type = 'identitycredential'; + } else if (vcFormatType == VCFormatType.jwtVc) { + type = 'individualverifiableattestation'; } link = '${Urls.id360Url}' diff --git a/lib/app/shared/extension/double_extension.dart b/lib/app/shared/extension/double_extension.dart new file mode 100644 index 000000000..aa3a7556e --- /dev/null +++ b/lib/app/shared/extension/double_extension.dart @@ -0,0 +1,14 @@ +extension DoubleExtension on double { + String decimalNumber(int n) { + int number = 1; + for (int i = 0; i < n; i++) { + if (i > toString().split('.').toList().last.length) { + break; + } + number *= 10; + } + + final twoDecimalNumber = (this * number).floor() / number; + return twoDecimalNumber.toString(); + } +} diff --git a/lib/app/shared/extension/extension.dart b/lib/app/shared/extension/extension.dart index d2dac2763..1703838c0 100644 --- a/lib/app/shared/extension/extension.dart +++ b/lib/app/shared/extension/extension.dart @@ -1,5 +1,6 @@ export 'bigint_extension.dart'; export 'credential_status.dart'; +export 'double_extension.dart'; export 'iterable_extension.dart'; export 'string_extension.dart'; export 'unit8List_extension.dart'; diff --git a/lib/app/shared/extension/string_extension.dart b/lib/app/shared/extension/string_extension.dart index d3b1f47f4..422fed68e 100644 --- a/lib/app/shared/extension/string_extension.dart +++ b/lib/app/shared/extension/string_extension.dart @@ -1,11 +1,12 @@ import 'dart:convert'; import 'package:convert/convert.dart'; +import 'package:decimal/decimal.dart'; import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; extension StringExtension on String { - String formatNumber() { + String get formatNumber { if (isEmpty || length < 3) return this; final formatter = NumberFormat('#,###'); final value = this; @@ -43,4 +44,26 @@ extension StringExtension on String { final String bytes = hex.encode(encode); return bytes; } + + bool get isEVM { + if (this == 'ETH' || this == 'MATIC' || this == 'FTM' || this == 'BNB') { + return true; + } + return false; + } + + String decimalNumber(int n) { + int number = 1; + for (int i = 0; i < n; i++) { + if (i > toString().split('.').toList().last.length) { + break; + } + number *= 10; + } + + final twoDecimalNumber = + (Decimal.parse(this) * Decimal.parse(number.toString())).floor() / + Decimal.parse(number.toString()); + return twoDecimalNumber.toDecimal().toString(); + } } diff --git a/lib/app/shared/helper_functions/helper_functions.dart b/lib/app/shared/helper_functions/helper_functions.dart index 19e28a4e5..057047a56 100644 --- a/lib/app/shared/helper_functions/helper_functions.dart +++ b/lib/app/shared/helper_functions/helper_functions.dart @@ -140,7 +140,12 @@ String getIssuersName(String constraints) { BlockchainType getBlockchainType(AccountType accountType) { switch (accountType) { case AccountType.ssi: - throw Exception(); + throw ResponseMessage( + data: { + 'error': 'invalid_request', + 'error_description': 'Invalid request.', + }, + ); case AccountType.tezos: return BlockchainType.tezos; case AccountType.ethereum: @@ -239,13 +244,6 @@ String getDateTimeWithoutSpace() { return dateTime; } -Future web3RpcMainnetInfuraURL() async { - await dotenv.load(); - final String infuraApiKey = dotenv.get('INFURA_API_KEY'); - const String prefixUrl = Parameters.web3RpcMainnetUrl; - return '$prefixUrl$infuraApiKey'; -} - int getIndexValue({ required bool isEBSIV3, required DidKeyType didKeyType, @@ -322,17 +320,24 @@ Future getPrivateKey({ .get(SecureStorageKeys.walletAttestationData); if (walletAttestationData == null) { - throw Exception(); + throw ResponseMessage( + data: { + 'error': 'invalid_request', + 'error_description': 'The wallet attestation data has some issue.', + }, + ); } final p256KeyForWallet = - await getWalletP256Key(profileCubit.secureStorageProvider); + await getP256KeyToGetAndPresentVC(profileCubit.secureStorageProvider); return p256KeyForWallet; } } -Future getWalletP256Key(SecureStorageProvider secureStorage) async { +Future getWalletAttestationP256Key( + SecureStorageProvider secureStorage, +) async { const storageKey = SecureStorageKeys.p256PrivateKeyForWallet; /// return key if it is already created @@ -347,6 +352,23 @@ Future getWalletP256Key(SecureStorageProvider secureStorage) async { return newKey; } +Future getP256KeyToGetAndPresentVC( + SecureStorageProvider secureStorage, +) async { + const storageKey = SecureStorageKeys.p256PrivateKeyToGetAndPresentVC; + + /// return key if it is already created + final String? p256PrivateKey = await secureStorage.get(storageKey); + if (p256PrivateKey != null) return p256PrivateKey.replaceAll('=', ''); + + /// create key if it is not created + final newKey = generateRandomP256Key(); + + await secureStorage.set(storageKey, newKey); + + return newKey; +} + String generateRandomP256Key() { /// create key if it is not created final jwk = JsonWebKey.generate('ES256'); @@ -535,7 +557,12 @@ Future<(String, String)> getDidAndKid({ .get(SecureStorageKeys.walletAttestationData); if (walletAttestationData == null) { - throw Exception(); + throw ResponseMessage( + data: { + 'error': 'invalid_request', + 'error_description': 'The wallet attestation data has some issue.', + }, + ); } final walletAttestationDataPayload = @@ -1005,7 +1032,13 @@ String getCredentialData(dynamic credential) { .toList(); cred = credentialSupported.last; } else { - throw Exception(); + throw ResponseMessage( + data: { + 'error': 'invalid_request', + 'error_description': + 'The format of credentail should be either String or Map.', + }, + ); } return cred; @@ -1030,7 +1063,14 @@ Future getHost({ scannedResponse: uri.toString(), dioClient: client, ); - if (credentialOfferJson == null) throw Exception(); + if (credentialOfferJson == null) { + throw ResponseMessage( + data: { + 'error': 'invalid_request', + 'error_description': 'The credential offer is required.', + }, + ); + } return Uri.parse( credentialOfferJson['credential_issuer'].toString(), @@ -1097,20 +1137,104 @@ MessageHandler getMessageHandler(dynamic e) { ); } else { final stringException = e.toString().replaceAll('Exception: ', ''); - if (stringException == 'CREDENTIAL_SUPPORT_DATA_ERROR') { + if (stringException.contains('CREDENTIAL_SUPPORT_DATA_ERROR')) { return ResponseMessage( data: { 'error': 'unsupported_credential_format', 'error_description': 'The credential support format has some issues.', }, ); - } else if (stringException == 'AUTHORIZATION_DETAIL_ERROR') { + } else if (stringException.contains('AUTHORIZATION_DETAIL_ERROR')) { return ResponseMessage( data: { 'error': 'unsupported_format', 'error_description': 'Invalid token response format.', }, ); + } else if (stringException.contains('INVALID_TOKEN')) { + return ResponseMessage( + data: { + 'error': 'invalid_format', + 'error_description': 'Failed to extract header from jwt.', + }, + ); + } else if (stringException.contains('INVALID_TOKEN')) { + return ResponseMessage( + data: { + 'error': 'invalid_format', + 'error_description': 'Failed to extract payload from jwt.', + }, + ); + } else if (stringException.contains('SSI_ISSUE')) { + return ResponseMessage( + data: { + 'error': 'invalid_format', + 'error_description': 'SSI does not support this process.', + }, + ); + } else if (stringException.contains('OPENID-CONFIGURATION-ISSUE')) { + return ResponseMessage( + data: { + 'error': 'unsupported_format', + 'error_description': 'Openid configuration response issue.', + }, + ); + } else if (stringException.contains('NOT_A_VALID_OPENID_URL')) { + return ResponseMessage( + data: { + 'error': 'unsupported_format', + 'error_description': 'Not a valid openid url to initiate issuance.', + }, + ); + } else if (stringException.contains('JWKS_URI_IS_NULL')) { + return ResponseMessage( + data: { + 'error': 'unsupported_format', + 'error_description': 'The jwks_uri is null.', + }, + ); + } else if (stringException.contains('Issue while getting')) { + return ResponseMessage( + data: { + 'error': 'invalid_request', + 'error_description': stringException, + }, + ); + } else if (stringException.contains('SECURE_STORAGE_ISSUE')) { + return ResponseMessage( + data: { + 'error': 'invalid_request', + 'error_description': 'Secure Storage issue with this device', + }, + ); + } else if (stringException.contains('ISSUE_WHILE_ADDING_IDENTITY')) { + return ResponseMessage( + data: { + 'error': 'invalid_request', + 'error_description': 'Issue while adding identity.', + }, + ); + } else if (stringException.contains('ISSUE_WHILE_GETTING_CLAIMS')) { + return ResponseMessage( + data: { + 'error': 'invalid_request', + 'error_description': 'Issue while getting claims.', + }, + ); + } else if (stringException.contains('ISSUE_WHILE_RESTORING_CLAIMS')) { + return ResponseMessage( + data: { + 'error': 'invalid_request', + 'error_description': 'Issue while restoring claims.', + }, + ); + } else if (stringException.contains('PUBLICKEYJWK_EXTRACTION_ERROR')) { + return ResponseMessage( + data: { + 'error': 'invalid_request', + 'error_description': 'Issue while restoring claims.', + }, + ); } else { return ResponseMessage( message: @@ -1455,11 +1579,8 @@ Future<(String?, String?, String?, String?)> getClientDetails({ final didKeyType = customOidc4vcProfile.defaultDid; - final privateKey = await fetchPrivateKey( - profileCubit: profileCubit, - isEBSIV3: isEBSIV3, - didKeyType: didKeyType, - ); + final privateKey = + await getP256KeyToGetAndPresentVC(profileCubit.secureStorageProvider); final (did, _) = await fetchDidAndKid( privateKey: privateKey, @@ -1473,7 +1594,7 @@ Future<(String?, String?, String?, String?)> getClientDetails({ did: '', // just added as it is required field mediaType: MediaType.basic, // just added as it is required field clientType: - ClientType.jwkThumbprint, // just added as it is required field + ClientType.p256JWKThumprint, // just added as it is required field proofHeaderType: customOidc4vcProfile.proofHeader, clientId: '', // just added as it is required field ); @@ -1488,7 +1609,7 @@ Future<(String?, String?, String?, String?)> getClientDetails({ .replaceAll('=', ''); switch (customOidc4vcProfile.clientType) { - case ClientType.jwkThumbprint: + case ClientType.p256JWKThumprint: clientId = tokenParameters.thumbprint; case ClientType.did: clientId = did; @@ -1499,7 +1620,7 @@ Future<(String?, String?, String?, String?)> getClientDetails({ /// only clientId case ClientAuthentication.clientId: switch (customOidc4vcProfile.clientType) { - case ClientType.jwkThumbprint: + case ClientType.p256JWKThumprint: clientId = tokenParameters.thumbprint; case ClientType.did: clientId = did; @@ -1992,3 +2113,31 @@ String? getWalletAddress(CredentialSubjectModel credentialSubjectModel) { } return null; } + +Future fetchRpcUrl(BlockchainNetwork blockchainNetwork) async { + String rpcUrl = ''; + + if (blockchainNetwork is BinanceNetwork || + blockchainNetwork is FantomNetwork) { + rpcUrl = blockchainNetwork.rpcNodeUrl as String; + } else { + if (blockchainNetwork.networkname == 'Mainnet') { + await dotenv.load(); + final String infuraApiKey = dotenv.get('INFURA_API_KEY'); + + late String prefixUrl; + + if (blockchainNetwork is PolygonNetwork) { + prefixUrl = Parameters.POLYGON_INFURA_URL; + } else { + prefixUrl = Parameters.web3RpcMainnetUrl; + } + + return '$prefixUrl$infuraApiKey'; + } else { + rpcUrl = blockchainNetwork.rpcNodeUrl as String; + } + } + + return rpcUrl; +} diff --git a/lib/app/shared/launch_url/launch_url.dart b/lib/app/shared/launch_url/launch_url.dart index 5c14e0de2..185954dbd 100644 --- a/lib/app/shared/launch_url/launch_url.dart +++ b/lib/app/shared/launch_url/launch_url.dart @@ -1,3 +1,4 @@ +import 'package:altme/app/app.dart'; import 'package:url_launcher/url_launcher.dart'; class LaunchUrl { @@ -7,7 +8,12 @@ class LaunchUrl { }) async { await canLaunchUrl(Uri.parse(url)) ? await launchUrl(Uri.parse(url), mode: launchMode) - : throw Exception('Could not launch $url'); + : throw ResponseMessage( + data: { + 'error': 'invalid_format', + 'error_description': 'Could not launch $url', + }, + ); } static Future launchUri( @@ -16,6 +22,11 @@ class LaunchUrl { }) async { await canLaunchUrl(uri) ? await launchUrl(uri, mode: launchMode) - : throw Exception('Could not launch $uri'); + : throw ResponseMessage( + data: { + 'error': 'invalid_format', + 'error_description': 'Could not launch $uri', + }, + ); } } diff --git a/lib/app/shared/m_web3_client/m_web3_client.dart b/lib/app/shared/m_web3_client/m_web3_client.dart index 52dd115ed..41ed3807c 100644 --- a/lib/app/shared/m_web3_client/m_web3_client.dart +++ b/lib/app/shared/m_web3_client/m_web3_client.dart @@ -4,13 +4,12 @@ import 'package:flutter/services.dart'; import 'package:http/http.dart' as http; import 'package:http/http.dart'; import 'package:web3dart/crypto.dart'; -import 'package:web3dart/json_rpc.dart'; import 'package:web3dart/web3dart.dart'; class MWeb3Client { static final log = getLogger('MWeb3Client'); - static Future getETHBalance({ + static Future getEVMBalance({ required String secretKey, required String rpcUrl, }) async { @@ -229,12 +228,12 @@ class MWeb3Client { return txId; } catch (e, s) { log.e('sendToken() error: $e , stack: $s'); - if (e is RPCError) rethrow; - return null; + rethrow; } } - static Future estimateEthereumFee({ + // maxGas, gasPrice, feeData + static Future<(BigInt, EtherAmount, BigInt)> estimateEVMFee({ required String web3RpcURL, required EthereumAddress sender, required EthereumAddress reciever, @@ -242,11 +241,9 @@ class MWeb3Client { String? data, }) async { log.i('estimateEthereumFee'); - late EtherAmount gasPrice = EtherAmount.inWei(BigInt.one); + final Web3Client web3Client = Web3Client(web3RpcURL, http.Client()); + final gasPrice = await web3Client.getGasPrice(); try { - final Web3Client web3Client = Web3Client(web3RpcURL, http.Client()); - gasPrice = await web3Client.getGasPrice(); - log.i('from: ${sender.hex}'); log.i('to: ${reciever.hex}'); log.i('gasPrice: ${gasPrice.getInWei}'); @@ -264,16 +261,18 @@ class MWeb3Client { final fee = maxGas * gasPrice.getInWei; log.i('maxGas * gasPrice.getInWei = $fee'); - return fee; + return (maxGas, gasPrice, fee); } catch (e, s) { log.e('e: $e, s: $s'); - final fee = BigInt.from(21000) * gasPrice.getInWei; + final maxGas = BigInt.from(21000); + final fee = maxGas * gasPrice.getInWei; + log.i('maxGas - $maxGas'); log.i('2100 * gasPrice.getInWei = $fee'); - return fee; + return (maxGas, gasPrice, fee); } } - static Future sendEthereumTransaction({ + static Future sendEVMTransaction({ required String web3RpcURL, required int chainId, required String privateKey, @@ -281,14 +280,16 @@ class MWeb3Client { required EthereumAddress reciever, required EtherAmount amount, BigInt? gas, + EtherAmount? gasPrice, String? data, }) async { log.i('sendEthereumTransaction'); final Web3Client web3Client = Web3Client(web3RpcURL, http.Client()); final int nonce = await web3Client.getTransactionCount(sender); - final EtherAmount gasPrice = await web3Client.getGasPrice(); - final maxGas = await web3Client.estimateGas( + gasPrice ??= await web3Client.getGasPrice(); + + gas ??= await web3Client.estimateGas( sender: sender, to: reciever, gasPrice: gasPrice, @@ -305,12 +306,15 @@ class MWeb3Client { value: amount, data: data != null ? hexToBytes(data) : null, nonce: nonce, - maxGas: maxGas.toInt(), + maxGas: gas.toInt(), ); log.i('nonce: $nonce'); - log.i('maxGas: ${maxGas.toInt()}'); + log.i('maxGas: ${gas.toInt()}'); log.i('chainId: $chainId'); + log.i('gasPrice: $gasPrice'); + final fee = gas * gasPrice.getInWei; + log.i('$gas * gasPrice.getInWei = $fee'); final transactionHash = await web3Client.sendTransaction( credentials, diff --git a/lib/chat_room/cubit/chat_room_cubit.dart b/lib/chat_room/cubit/chat_room_cubit.dart index ee43d9d3a..fb2daa104 100644 --- a/lib/chat_room/cubit/chat_room_cubit.dart +++ b/lib/chat_room/cubit/chat_room_cubit.dart @@ -264,7 +264,8 @@ abstract class ChatRoomCubit extends Cubit { Future _checkIfRoomNotExistThenCreateIt() async { if (_roomId == null || _roomId!.isEmpty) { - final p256KeyForWallet = await getWalletP256Key(secureStorageProvider); + final p256KeyForWallet = + await getP256KeyToGetAndPresentVC(secureStorageProvider); final customOidc4vcProfile = profileCubit.state.model.profileSetting .selfSovereignIdentityOptions.customOidc4vcProfile; @@ -274,7 +275,7 @@ abstract class ChatRoomCubit extends Cubit { did: '', // just added as it is required field mediaType: MediaType.basic, // just added as it is required field clientType: - ClientType.jwkThumbprint, // just added as it is required field + ClientType.p256JWKThumprint, // just added as it is required field proofHeaderType: customOidc4vcProfile.proofHeader, clientId: customOidc4vcProfile.clientId ?? '', ); diff --git a/lib/credentials/cubit/credentials_cubit.dart b/lib/credentials/cubit/credentials_cubit.dart index 80a6835a2..9a94dd22f 100644 --- a/lib/credentials/cubit/credentials_cubit.dart +++ b/lib/credentials/cubit/credentials_cubit.dart @@ -507,7 +507,15 @@ class CredentialsCubit extends Cubit { final supportAssociatedCredential = supportCryptoCredential(profileCubit.state.model); - if (!supportAssociatedCredential) throw Exception(); + if (!supportAssociatedCredential) { + throw ResponseMessage( + data: { + 'error': 'invalid_request', + 'error_description': + 'The crypto associated credential is not supported.', + }, + ); + } final didKeyType = profileCubit.state.model.profileSetting .selfSovereignIdentityOptions.customOidc4vcProfile.defaultDid; @@ -601,7 +609,12 @@ class CredentialsCubit extends Cubit { final did = oldCredential.credentialPreview.credentialSubjectModel.id; if (did == null) { - throw Exception(); + throw ResponseMessage( + data: { + 'error': 'invalid_request', + 'error_description': 'DID is required.', + }, + ); } final didKeyType = profileCubit.state.model.profileSetting @@ -711,7 +724,8 @@ class CredentialsCubit extends Cubit { final displayVerifiableId = vcFormatType == VCFormatType.ldpVc && discoverCardsOptions.displayVerifiableId; final displayVerifiableIdJwt = - vcFormatType == VCFormatType.jwtVcJson && + (vcFormatType == VCFormatType.jwtVc || + vcFormatType == VCFormatType.jwtVcJson) && discoverCardsOptions.displayVerifiableIdJwt; final displayVerifiableIdSdJwt = vcFormatType == VCFormatType.vcSdJWT && diff --git a/lib/dashboard/add_account/create_account/view/congratulations_account_creation_page.dart b/lib/dashboard/add_account/create_account/view/congratulations_account_creation_page.dart index d6699e44a..a31ac53ef 100644 --- a/lib/dashboard/add_account/create_account/view/congratulations_account_creation_page.dart +++ b/lib/dashboard/add_account/create_account/view/congratulations_account_creation_page.dart @@ -71,7 +71,12 @@ class _CongratulationsAccountCreationViewState String message = ''; switch (widget.accountType) { case AccountType.ssi: - throw Exception(); + throw ResponseMessage( + data: { + 'error': 'invalid_request', + 'error_description': 'Invalid request.', + }, + ); case AccountType.tezos: message = l10n.tezosAccountCreationCongratulations; diff --git a/lib/dashboard/add_account/import_account/cubit/import_account_cubit.dart b/lib/dashboard/add_account/import_account/cubit/import_account_cubit.dart index a91ac6068..8766896ad 100644 --- a/lib/dashboard/add_account/import_account/cubit/import_account_cubit.dart +++ b/lib/dashboard/add_account/import_account/cubit/import_account_cubit.dart @@ -63,7 +63,14 @@ class ImportAccountCubit extends Cubit { final String? mnemonicOrKey = await getSecureStorage .get(SecureStorageKeys.importAccountStep2Mnemonics); - if (mnemonicOrKey == null) throw Exception(); + if (mnemonicOrKey == null) { + throw ResponseMessage( + data: { + 'error': 'invalid_request', + 'error_description': 'Please provide the mnemonics or private key.', + }, + ); + } await walletCubit.createCryptoWallet( accountName: accountName, diff --git a/lib/dashboard/connection/operation/cubit/operation_cubit.dart b/lib/dashboard/connection/operation/cubit/operation_cubit.dart index d2e2d9eba..97b122408 100644 --- a/lib/dashboard/connection/operation/cubit/operation_cubit.dart +++ b/lib/dashboard/connection/operation/cubit/operation_cubit.dart @@ -4,7 +4,7 @@ import 'dart:math'; import 'package:altme/app/app.dart'; import 'package:altme/connection_bridge/connection_bridge.dart'; -import 'package:altme/dashboard/home/home.dart'; +import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/wallet/wallet.dart'; import 'package:beacon_flutter/beacon_flutter.dart'; import 'package:bloc/bloc.dart'; @@ -29,6 +29,7 @@ class OperationCubit extends Cubit { required this.tokensCubit, required this.walletConnectCubit, required this.connectedDappRepository, + required this.manageNetworkCubit, }) : super(const OperationState()); final WalletCubit walletCubit; @@ -40,6 +41,7 @@ class OperationCubit extends Cubit { final TokensCubit tokensCubit; final WalletConnectCubit walletConnectCubit; final ConnectedDappRepository connectedDappRepository; + final ManageNetworkCubit manageNetworkCubit; final log = getLogger('OperationCubit'); @@ -206,10 +208,11 @@ class OperationCubit extends Cubit { walletConnectCubit.state.transaction!.value!; amount = MWeb3Client.formatEthAmount(amount: ethAmount.getInWei); - final String web3RpcURL = await web3RpcMainnetInfuraURL(); + final web3RpcURL = + await fetchRpcUrl(manageNetworkCubit.state.network); log.i('web3RpcURL - $web3RpcURL'); - final feeData = await MWeb3Client.estimateEthereumFee( + final (_, _, feeData) = await MWeb3Client.estimateEVMFee( web3RpcURL: web3RpcURL, sender: walletConnectCubit.state.transaction!.from!, reciever: walletConnectCubit.state.transaction!.to!, @@ -346,25 +349,11 @@ class OperationCubit extends Cubit { final EtherAmount ethAmount = walletConnectCubit.state.transaction!.value!; - late String rpcUrl; - - switch (transactionAccountData.blockchainType) { - case BlockchainType.tezos: - throw Exception(); - case BlockchainType.ethereum: - rpcUrl = await web3RpcMainnetInfuraURL(); - case BlockchainType.fantom: - rpcUrl = FantomNetwork.mainNet().rpcNodeUrl as String; - case BlockchainType.polygon: - rpcUrl = PolygonNetwork.mainNet().rpcNodeUrl as String; - case BlockchainType.binance: - rpcUrl = BinanceNetwork.mainNet().rpcNodeUrl as String; - } + final rpcUrl = await fetchRpcUrl(manageNetworkCubit.state.network); log.i('rpcUrl - $rpcUrl'); - final String transactionHash = - await MWeb3Client.sendEthereumTransaction( + final String transactionHash = await MWeb3Client.sendEVMTransaction( chainId: transactionAccountData.blockchainType.chainId, web3RpcURL: rpcUrl, privateKey: transactionAccountData.secretKey, diff --git a/lib/dashboard/connection/operation/view/operation_page.dart b/lib/dashboard/connection/operation/view/operation_page.dart index bed7ea174..1624bba88 100644 --- a/lib/dashboard/connection/operation/view/operation_page.dart +++ b/lib/dashboard/connection/operation/view/operation_page.dart @@ -46,6 +46,7 @@ class OperationPage extends StatelessWidget { tokensCubit: context.read(), walletConnectCubit: context.read(), connectedDappRepository: ConnectedDappRepository(getSecureStorage), + manageNetworkCubit: context.read(), ), child: OperationView(connectionBridgeType: connectionBridgeType), ); @@ -179,7 +180,7 @@ class _OperationViewState extends State { ), const SizedBox(height: Sizes.spaceSmall), MyText( - '''${state.amount.toStringAsFixed(6).formatNumber()} ${symbol ?? ''}''', + '''${state.amount.decimalNumber(6).formatNumber} ${symbol ?? ''}''', textAlign: TextAlign.center, style: Theme.of(context) .textTheme diff --git a/lib/dashboard/connection/operation/widgets/fee_details.dart b/lib/dashboard/connection/operation/widgets/fee_details.dart index ca283b466..6d51aa750 100644 --- a/lib/dashboard/connection/operation/widgets/fee_details.dart +++ b/lib/dashboard/connection/operation/widgets/fee_details.dart @@ -36,7 +36,7 @@ class FeeDetails extends StatelessWidget { ), const Spacer(), Text( - '${amount.toStringAsFixed(6).formatNumber()} $symbol', + '${amount.decimalNumber(6).formatNumber} $symbol', style: Theme.of(context).textTheme.bodySmall, ), ], @@ -50,7 +50,7 @@ class FeeDetails extends StatelessWidget { ), const Spacer(), Text( - '${fee.toStringAsFixed(6).formatNumber()} $symbol', + '${fee.decimalNumber(6).formatNumber} $symbol', style: Theme.of(context).textTheme.bodySmall, ), ], @@ -69,15 +69,15 @@ class FeeDetails extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.end, children: [ Text( - '''${grandTotal.toStringAsFixed(6).formatNumber()} $symbol''', + '''${grandTotal.decimalNumber(6).formatNumber} $symbol''', style: Theme.of(context).textTheme.bodySmall, ), if (tokenUSDRate > 0) Text( r'$' + (grandTotal * tokenUSDRate) - .toStringAsFixed(6) - .formatNumber(), + .decimalNumber(6) + .formatNumber, style: Theme.of(context).textTheme.bodySmall2, ), ], diff --git a/lib/dashboard/discover/view/discover_tab_page.dart b/lib/dashboard/discover/view/discover_tab_page.dart index b2a6a8291..373697b7a 100644 --- a/lib/dashboard/discover/view/discover_tab_page.dart +++ b/lib/dashboard/discover/view/discover_tab_page.dart @@ -146,7 +146,7 @@ class _DiscoverTabPageViewState extends State return NavigationDecision.navigate; } }, - ) + ), ], ), ), diff --git a/lib/dashboard/drawer/ssi/manage_did/manage_did.dart b/lib/dashboard/drawer/ssi/manage_did/manage_did.dart index f94a8ccd7..cc711a3f9 100644 --- a/lib/dashboard/drawer/ssi/manage_did/manage_did.dart +++ b/lib/dashboard/drawer/ssi/manage_did/manage_did.dart @@ -1,4 +1,5 @@ export 'view/did/did_private_key_page.dart'; export 'view/did/manage_did_page.dart'; export 'view/did_polygon_id/manage_did_polygon_id_page.dart'; +export 'view/jwk_thumbprint_p256_key/jwk_thumbprint_p256_key_page.dart'; export 'widgets/widgets.dart'; diff --git a/lib/dashboard/drawer/ssi/manage_did/view/did_menu.dart b/lib/dashboard/drawer/ssi/manage_did/view/did_menu.dart index 4e32543da..2739c9f75 100644 --- a/lib/dashboard/drawer/ssi/manage_did/view/did_menu.dart +++ b/lib/dashboard/drawer/ssi/manage_did/view/did_menu.dart @@ -97,6 +97,14 @@ class DidView extends StatelessWidget { } }, ), + DrawerItem( + title: l10n.jwkThumbprintP256Key, + onTap: () { + Navigator.of(context).push( + JWKThumbprintP256KeyPage.route(), + ); + }, + ), ], ), ), diff --git a/lib/dashboard/drawer/ssi/manage_did/view/jwk_thumbprint_p256_key/jwk_thumbprint_p256_key_page.dart b/lib/dashboard/drawer/ssi/manage_did/view/jwk_thumbprint_p256_key/jwk_thumbprint_p256_key_page.dart new file mode 100644 index 000000000..ff1440130 --- /dev/null +++ b/lib/dashboard/drawer/ssi/manage_did/view/jwk_thumbprint_p256_key/jwk_thumbprint_p256_key_page.dart @@ -0,0 +1,140 @@ +import 'package:altme/app/app.dart'; +import 'package:altme/dashboard/dashboard.dart'; +import 'package:altme/l10n/l10n.dart'; +import 'package:altme/theme/theme.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +class JWKThumbprintP256KeyPage extends StatefulWidget { + const JWKThumbprintP256KeyPage({ + super.key, + }); + + static Route route() { + return MaterialPageRoute( + builder: (_) => const JWKThumbprintP256KeyPage(), + settings: const RouteSettings(name: '/JWKThumbprintP256KeyPage'), + ); + } + + @override + State createState() => + _JWKThumbprintP256KeyPageState(); +} + +class _JWKThumbprintP256KeyPageState extends State + with SingleTickerProviderStateMixin { + late Animation animation; + late AnimationController animationController; + + Future getKey() async { + final privateKey = await getP256KeyToGetAndPresentVC( + context.read().secureStorageProvider, + ); + return privateKey; + } + + @override + void initState() { + super.initState(); + animationController = AnimationController( + vsync: this, + duration: const Duration(seconds: 20), + ); + + final Tween rotationTween = Tween(begin: 20, end: 0); + + animation = rotationTween.animate(animationController) + ..addStatusListener((status) { + if (status == AnimationStatus.completed) { + Navigator.pop(context); + } + }); + animationController.forward(); + } + + @override + void dispose() { + animationController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final l10n = context.l10n; + return BasePage( + scrollView: false, + title: l10n.jwkThumbprintP256Key, + titleAlignment: Alignment.topCenter, + titleLeading: const BackLeadingButton(), + secureScreen: true, + body: BackgroundCard( + width: double.infinity, + height: double.infinity, + child: Column( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Text( + l10n.jwkThumbprintP256Key, + style: Theme.of(context).textTheme.title, + ), + const SizedBox( + height: Sizes.spaceNormal, + ), + FutureBuilder( + future: getKey(), + builder: (context, snapshot) { + switch (snapshot.connectionState) { + case ConnectionState.done: + return Column( + children: [ + Text( + snapshot.data!, + textAlign: TextAlign.center, + style: Theme.of(context).textTheme.titleMedium, + ), + const SizedBox(height: Sizes.spaceXLarge), + CopyButton( + onTap: () async { + await Clipboard.setData( + ClipboardData(text: snapshot.data!), + ); + AlertMessage.showStateMessage( + context: context, + stateMessage: StateMessage.success( + stringMessage: l10n.copiedToClipboard, + ), + ); + }, + ), + ], + ); + case ConnectionState.waiting: + case ConnectionState.none: + case ConnectionState.active: + return const Text(''); + } + }, + ), + Expanded( + child: Center( + child: AnimatedBuilder( + animation: animation, + builder: (BuildContext context, Widget? child) { + return Text( + timeFormatter(timeInSecond: animation.value.toInt()), + textAlign: TextAlign.center, + style: Theme.of(context).textTheme.displayMedium, + ); + }, + ), + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/dashboard/drawer/ssi/oidc4vc_settngs/view/oidc4vc_settings_menu.dart b/lib/dashboard/drawer/ssi/oidc4vc_settngs/view/oidc4vc_settings_menu.dart index 2e185ddc9..f0b5a6433 100644 --- a/lib/dashboard/drawer/ssi/oidc4vc_settngs/view/oidc4vc_settings_menu.dart +++ b/lib/dashboard/drawer/ssi/oidc4vc_settngs/view/oidc4vc_settings_menu.dart @@ -44,7 +44,7 @@ class Oidc4vcSettingMenuView extends StatelessWidget { const ScopeParameterWidget(), const ClientAuthenticationWidget(), const ClientTypeWidget(), - const ClientCredentialsWidget(), + const ConfidentialClientWidget(), const VCFormatWidget(), const ProofTypeWidget(), const ProofHeaderWidget(), diff --git a/lib/dashboard/drawer/ssi/oidc4vc_settngs/widget/client_credentials_widget.dart b/lib/dashboard/drawer/ssi/oidc4vc_settngs/widget/confidential_client_widget.dart similarity index 98% rename from lib/dashboard/drawer/ssi/oidc4vc_settngs/widget/client_credentials_widget.dart rename to lib/dashboard/drawer/ssi/oidc4vc_settngs/widget/confidential_client_widget.dart index 9337b794b..11430ac91 100644 --- a/lib/dashboard/drawer/ssi/oidc4vc_settngs/widget/client_credentials_widget.dart +++ b/lib/dashboard/drawer/ssi/oidc4vc_settngs/widget/confidential_client_widget.dart @@ -5,8 +5,8 @@ import 'package:altme/theme/theme.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -class ClientCredentialsWidget extends StatelessWidget { - const ClientCredentialsWidget({super.key}); +class ConfidentialClientWidget extends StatelessWidget { + const ConfidentialClientWidget({super.key}); @override Widget build(BuildContext context) { @@ -27,7 +27,7 @@ class ClientCredentialsWidget extends StatelessWidget { ); return OptionContainer( - title: l10n.clientCredentials, + title: l10n.confidentialClient, body: Padding( padding: const EdgeInsets.symmetric(horizontal: 10), child: Column( diff --git a/lib/dashboard/drawer/ssi/oidc4vc_settngs/widget/widget.dart b/lib/dashboard/drawer/ssi/oidc4vc_settngs/widget/widget.dart index 8fba43ada..fd70d3be3 100644 --- a/lib/dashboard/drawer/ssi/oidc4vc_settngs/widget/widget.dart +++ b/lib/dashboard/drawer/ssi/oidc4vc_settngs/widget/widget.dart @@ -1,6 +1,6 @@ export 'client_authentication_widget.dart'; -export 'client_credentials_widget.dart'; export 'client_type_widget.dart'; +export 'confidential_client_widget.dart'; export 'cryptograhic_holder_binding.dart'; export 'did_key_type_widget.dart'; export 'draft_type_widget.dart'; diff --git a/lib/dashboard/home/home/widgets/token_reward_dialog.dart b/lib/dashboard/home/home/widgets/token_reward_dialog.dart index 08ce76f89..3a33d3c5b 100644 --- a/lib/dashboard/home/home/widgets/token_reward_dialog.dart +++ b/lib/dashboard/home/home/widgets/token_reward_dialog.dart @@ -59,7 +59,7 @@ class TokenRewardDialog extends StatelessWidget { children: [ TextSpan( text: - ''' ${tokenReward.amount.toString().formatNumber()} ${tokenReward.symbol} ''', + ''' ${tokenReward.amount.toString().formatNumber} ${tokenReward.symbol} ''', style: Theme.of(context).textTheme.defaultDialogBody.copyWith( fontWeight: FontWeight.w900, diff --git a/lib/dashboard/home/tab_bar/credentials/list/view/home_credentials_list_page.dart b/lib/dashboard/home/tab_bar/credentials/list/view/home_credentials_list_page.dart index cf4bc9077..7a32e9867 100644 --- a/lib/dashboard/home/tab_bar/credentials/list/view/home_credentials_list_page.dart +++ b/lib/dashboard/home/tab_bar/credentials/list/view/home_credentials_list_page.dart @@ -45,11 +45,20 @@ class _HomeCredentialsListPageState extends State padding: EdgeInsets.zero, backgroundColor: Theme.of(context).colorScheme.transparent, body: BlocListener( - listenWhen: (previous, current) => - current.model.profileSetting.selfSovereignIdentityOptions - .customOidc4vcProfile.vcFormatType != - previous.model.profileSetting.selfSovereignIdentityOptions - .customOidc4vcProfile.vcFormatType, + listenWhen: (previous, current) { + if (current.model.profileSetting.selfSovereignIdentityOptions + .customOidc4vcProfile.vcFormatType != + previous.model.profileSetting.selfSovereignIdentityOptions + .customOidc4vcProfile.vcFormatType) { + return true; + } + + if (current.model.isDeveloperMode != previous.model.isDeveloperMode) { + return true; + } + + return false; + }, listener: (context, state) { onRefresh(); }, diff --git a/lib/dashboard/home/tab_bar/credentials/list/widgets/home_credential_category_list.dart b/lib/dashboard/home/tab_bar/credentials/list/widgets/home_credential_category_list.dart index 9a9d95049..0b9a9dbff 100644 --- a/lib/dashboard/home/tab_bar/credentials/list/widgets/home_credential_category_list.dart +++ b/lib/dashboard/home/tab_bar/credentials/list/widgets/home_credential_category_list.dart @@ -19,14 +19,10 @@ class HomeCredentialCategoryList extends StatelessWidget { Widget build(BuildContext context) { return BlocBuilder( builder: (context, advanceSettingsState) { - final vcFormatType = context - .read() - .state - .model - .profileSetting - .selfSovereignIdentityOptions - .customOidc4vcProfile - .vcFormatType; + final profileModel = context.read().state.model; + + final customOidc4vcProfile = profileModel + .profileSetting.selfSovereignIdentityOptions.customOidc4vcProfile; return RefreshIndicator( onRefresh: onRefresh, @@ -53,7 +49,9 @@ class HomeCredentialCategoryList extends StatelessWidget { if (element.credentialPreview.credentialSubjectModel .credentialSubjectType == CredentialSubjectType.walletCredential) { - return true; + if (profileModel.isDeveloperMode) { + return true; + } } /// crypto credential account to be shown always @@ -78,7 +76,8 @@ class HomeCredentialCategoryList extends StatelessWidget { } /// do not load the credential if vc format is different - if (vcFormatType.value != element.getFormat) { + if (customOidc4vcProfile.vcFormatType.value != + element.getFormat) { return false; } diff --git a/lib/dashboard/home/tab_bar/credentials/present/pick/credential_manifest/cubit/credential_manifest_pick_cubit.dart b/lib/dashboard/home/tab_bar/credentials/present/pick/credential_manifest/cubit/credential_manifest_pick_cubit.dart index 678c6d097..2e6bafd01 100644 --- a/lib/dashboard/home/tab_bar/credentials/present/pick/credential_manifest/cubit/credential_manifest_pick_cubit.dart +++ b/lib/dashboard/home/tab_bar/credentials/present/pick/credential_manifest/cubit/credential_manifest_pick_cubit.dart @@ -117,10 +117,24 @@ class CredentialManifestPickCubit extends Cubit { } else if (atLeast != null) { isButtonEnabled = selected.length >= atLeast; } else { - throw Exception(); + throw ResponseMessage( + data: { + 'error': 'invalid_format', + 'error_description': + 'The count or min parameter should be provided in the ' + 'submissionRequirements of presentation_definition.', + }, + ); } } else { - throw Exception(); + throw ResponseMessage( + data: { + 'error': 'invalid_format', + 'error_description': + 'The submissionRequirements should be provided in the ' + 'presentation_definition.', + }, + ); } } else { /// normal case diff --git a/lib/dashboard/home/tab_bar/credentials/present/pick/selective_disclosure/cubit/selective_disclosure_pick_cubit.dart b/lib/dashboard/home/tab_bar/credentials/present/pick/selective_disclosure/cubit/selective_disclosure_pick_cubit.dart index fc9551688..3c1f5fca8 100644 --- a/lib/dashboard/home/tab_bar/credentials/present/pick/selective_disclosure/cubit/selective_disclosure_pick_cubit.dart +++ b/lib/dashboard/home/tab_bar/credentials/present/pick/selective_disclosure/cubit/selective_disclosure_pick_cubit.dart @@ -98,7 +98,12 @@ class SelectiveDisclosureCubit extends Cubit { } if (index == null) { - throw Exception(); + throw ResponseMessage( + data: { + 'error': 'invalid_request', + 'error_description': 'Issue with the dislosuer of jwt.', + }, + ); } final bool isSelected = state.selectedSDIndexInJWT.contains(index); diff --git a/lib/dashboard/home/tab_bar/credentials/present/pick/selective_disclosure/view/selective_disclosure_pick_page.dart b/lib/dashboard/home/tab_bar/credentials/present/pick/selective_disclosure/view/selective_disclosure_pick_page.dart index e04e17a05..bd868cc05 100644 --- a/lib/dashboard/home/tab_bar/credentials/present/pick/selective_disclosure/view/selective_disclosure_pick_page.dart +++ b/lib/dashboard/home/tab_bar/credentials/present/pick/selective_disclosure/view/selective_disclosure_pick_page.dart @@ -243,7 +243,7 @@ class _SelectiveDisclosurePickViewState did: '', // just added as it is required field mediaType: MediaType.selectiveDisclosure, clientType: - ClientType.jwkThumbprint, // just added as it is required field + ClientType.p256JWKThumprint, // just added as it is required field proofHeaderType: customOidc4vcProfile.proofHeader, clientId: '', // just added as it is required field ); @@ -284,7 +284,12 @@ class _SelectiveDisclosurePickViewState qrCodeScanCubit: context.read(), ); } else { - throw Exception(); + throw ResponseMessage( + data: { + 'error': 'invalid_request', + 'error_description': 'Issue with the disclosure encryption of jwt.', + }, + ); } } } diff --git a/lib/dashboard/home/tab_bar/nft/view/nft_details_page.dart b/lib/dashboard/home/tab_bar/nft/view/nft_details_page.dart index c913128c1..785117eea 100644 --- a/lib/dashboard/home/tab_bar/nft/view/nft_details_page.dart +++ b/lib/dashboard/home/tab_bar/nft/view/nft_details_page.dart @@ -411,7 +411,7 @@ class SendButton extends StatelessWidget { ? (widget.nftModel as TezosNftModel).getToken() : (widget.nftModel as EthereumNftModel).getToken(), withdrawalAddress: '', - amount: 1, + amount: '1', isNFT: true, ), ); diff --git a/lib/dashboard/home/tab_bar/tokens/confirm_token_transaction/cubit/confirm_token_transaction_cubit.dart b/lib/dashboard/home/tab_bar/tokens/confirm_token_transaction/cubit/confirm_token_transaction_cubit.dart index af04de485..d0b3c1979 100644 --- a/lib/dashboard/home/tab_bar/tokens/confirm_token_transaction/cubit/confirm_token_transaction_cubit.dart +++ b/lib/dashboard/home/tab_bar/tokens/confirm_token_transaction/cubit/confirm_token_transaction_cubit.dart @@ -5,8 +5,10 @@ import 'dart:math'; import 'package:altme/app/app.dart'; import 'package:altme/dashboard/dashboard.dart'; +import 'package:altme/wallet/wallet.dart'; import 'package:bloc/bloc.dart'; import 'package:dartez/dartez.dart'; +import 'package:decimal/decimal.dart'; import 'package:equatable/equatable.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'package:key_generator/key_generator.dart'; @@ -22,11 +24,13 @@ class ConfirmTokenTransactionCubit extends Cubit { required this.manageNetworkCubit, required this.client, required this.keyGenerator, + required this.walletCubit, }) : super(initialState); final ManageNetworkCubit manageNetworkCubit; final DioClient client; final KeyGenerator keyGenerator; + final WalletCubit walletCubit; final logger = getLogger('ConfirmWithdrawal'); @@ -57,10 +61,11 @@ class ConfirmTokenTransactionCubit extends Cubit { ); } - Future getEthUSDPrice() async { + Future getEVMUSDPrice(BlockchainType blockchainType) async { try { + final symbol = blockchainType.symbol; final dynamic response = await client.get( - '${Urls.cryptoCompareBaseUrl}/data/price?fsym=ETH&tsyms=USD', + '${Urls.cryptoCompareBaseUrl}/data/price?fsym=$symbol&tsyms=USD', ); if (response['USD'] != null) { final tokenUSDPrice = response['USD'] as double; @@ -80,7 +85,7 @@ class ConfirmTokenTransactionCubit extends Cubit { await _calculateFeeTezos(); unawaited(getXtzUSDPrice()); } else { - await _calculateFeeEthereum(); + await _calculateEVMFee(); } } catch (e, s) { logger.i('error: $e , stack: $s'); @@ -89,36 +94,54 @@ class ConfirmTokenTransactionCubit extends Cubit { } } - Future _calculateFeeEthereum() async { - final web3RpcURL = await web3RpcMainnetInfuraURL(); + BigInt? gas; + EtherAmount? gasPrice; - final amount = state.tokenAmount - .toStringAsFixed(int.parse(state.selectedToken.decimals)) - .replaceAll(',', '') - .replaceAll('.', ''); + Future _calculateEVMFee() async { + gas = null; + gasPrice = null; + final blockchainType = walletCubit.state.currentAccount?.blockchainType; + + if (blockchainType == null) { + throw ResponseMessage( + data: { + 'error': 'invalid_request', + 'error_description': 'Please set the blockchain type first.', + }, + ); + } + + final amount = + Decimal.parse(state.tokenAmount.replaceAll('.', '').replaceAll(',', '')) + .toBigInt(); final credentials = EthPrivateKey.fromHex(state.selectedAccountSecretKey); final sender = credentials.address; final reciever = EthereumAddress.fromHex(state.withdrawalAddress); - final maxGas = await MWeb3Client.estimateEthereumFee( + final web3RpcURL = await fetchRpcUrl(manageNetworkCubit.state.network); + + final (maxGas, priceOfGas, feeData) = await MWeb3Client.estimateEVMFee( web3RpcURL: web3RpcURL, sender: sender, reciever: reciever, amount: EtherAmount.inWei( - state.selectedToken.symbol == 'ETH' - ? BigInt.from(double.parse(amount)) - : BigInt.zero, + state.selectedToken.symbol.isEVM ? amount : BigInt.zero, ), ); - final fee = EtherAmount.inWei(maxGas).getValueInUnit(EtherUnit.ether); - final etherUSDPrice = (await getEthUSDPrice()) ?? 0; + gas = maxGas; + gasPrice = priceOfGas; + + final fee = EtherAmount.inWei(feeData).getValueInUnit(EtherUnit.ether); + + final etherUSDPrice = (await getEVMUSDPrice(blockchainType)) ?? 0; final double feeInUSD = etherUSDPrice * fee; + final networkFee = NetworkFeeModel( fee: fee, networkSpeed: NetworkSpeed.average, - tokenSymbol: 'ETH', + tokenSymbol: blockchainType.symbol, feeInUSD: feeInUSD, ); @@ -127,7 +150,9 @@ class ConfirmTokenTransactionCubit extends Cubit { status: AppStatus.init, networkFee: networkFee, totalAmount: state.selectedToken.symbol == networkFee.tokenSymbol - ? state.tokenAmount - networkFee.fee + ? (Decimal.parse(state.tokenAmount) - + Decimal.parse(networkFee.fee.toString())) + .toString() : state.tokenAmount, ), ); @@ -188,7 +213,9 @@ class ConfirmTokenTransactionCubit extends Cubit { networkFees: tezosNetworkFees, status: AppStatus.init, totalAmount: state.selectedToken.symbol == networkFee.tokenSymbol - ? state.tokenAmount - networkFee.fee + ? (Decimal.parse(state.tokenAmount) - + Decimal.parse(networkFee.fee.toString())) + .toString() : state.tokenAmount, operationsList: finalOperationList, ), @@ -225,7 +252,7 @@ class ConfirmTokenTransactionCubit extends Cubit { contractAddress: state.selectedToken.contractAddress, rpcInterface: client.rpcInterface, ); - final amount = (state.tokenAmount * 1000000).toInt(); + final amount = (double.parse(state.tokenAmount) * 1000000).toInt(); final parameters = state.selectedToken.isFA1 ? '''(Pair "${keystore.publicKey}" (Pair "${state.withdrawalAddress}" $amount))''' : '''{Pair "${keystore.publicKey}" {Pair "${state.withdrawalAddress}" (Pair ${int.parse(state.selectedToken.tokenId ?? '0')} $amount)}}'''; @@ -246,7 +273,8 @@ class ConfirmTokenTransactionCubit extends Cubit { ) async { const minFeesWithStorage = 6526; var transactionAmount = - ((state.tokenAmount * 1000000).toInt()) - minFeesWithStorage; + ((double.parse(state.tokenAmount) * 1000000).toInt()) - + minFeesWithStorage; // Get fees final OperationsList estimationOperationList = await prepareTezosOperation(keystore, client, transactionAmount); @@ -293,7 +321,9 @@ class ConfirmTokenTransactionCubit extends Cubit { void setNetworkFee({required NetworkFeeModel networkFee}) { final totalAmount = state.selectedToken.symbol == networkFee.tokenSymbol - ? state.tokenAmount - networkFee.fee + ? (Decimal.parse(state.tokenAmount) - + Decimal.parse(networkFee.fee.toString())) + .toString() : state.tokenAmount; emit( @@ -305,7 +335,7 @@ class ConfirmTokenTransactionCubit extends Cubit { } bool canConfirmTheWithdrawal() { - return state.totalAmount > 0.0 && + return Decimal.parse(state.totalAmount) > Decimal.parse('0') && state.withdrawalAddress.trim().isNotEmpty && state.status != AppStatus.loading; } @@ -327,49 +357,93 @@ class ConfirmTokenTransactionCubit extends Cubit { } Future _withdrawEthereumBaseTokenByChainId({ - required double tokenAmount, + required String tokenAmount, required String selectedAccountSecretKey, }) async { - try { - emit(state.loading()); - final selectedEthereumNetwork = - manageNetworkCubit.state.network as EthereumNetwork; + emit(state.loading()); + final selectedEthereumNetwork = + manageNetworkCubit.state.network as EthereumNetwork; + + final rpcNodeUrl = selectedEthereumNetwork.rpcNodeUrl as String; + + final amount = BigInt.parse( + tokenAmount + .decimalNumber( + int.parse(selectedEthereumNetwork.mainTokenDecimal), + ) + .replaceAll('.', '') + .replaceAll(',', ''), + ); - final rpcNodeUrl = selectedEthereumNetwork.rpcNodeUrl as String; + getLogger(toString()) + .i('selected network node rpc: $rpcNodeUrl, amount: $tokenAmount'); - final amount = int.parse( - tokenAmount - .toStringAsFixed( - int.parse(selectedEthereumNetwork.mainTokenDecimal), - ) - .replaceAll(',', '') - .replaceAll('.', ''), - ); + final credentials = EthPrivateKey.fromHex(selectedAccountSecretKey); + final sender = credentials.address; - getLogger(toString()) - .i('selected network node rpc: $rpcNodeUrl, amount: $tokenAmount'); + final transactionHash = await MWeb3Client.sendEVMTransaction( + web3RpcURL: rpcNodeUrl, + chainId: selectedEthereumNetwork.chainId, + privateKey: selectedAccountSecretKey, + sender: sender, + reciever: EthereumAddress.fromHex(state.withdrawalAddress), + amount: EtherAmount.inWei(amount), + gas: gas, + gasPrice: gasPrice, + ); - final credentials = EthPrivateKey.fromHex(selectedAccountSecretKey); - final sender = credentials.address; + logger.i( + 'sending from: $sender to : ${state.withdrawalAddress},etherAmountInWei: ${EtherAmount.inWei(amount)}', + ); - final transactionHash = await MWeb3Client.sendEthereumTransaction( - web3RpcURL: rpcNodeUrl, - chainId: selectedEthereumNetwork.chainId, - privateKey: selectedAccountSecretKey, - sender: sender, - reciever: EthereumAddress.fromHex(state.withdrawalAddress), - amount: EtherAmount.inWei(BigInt.from(amount)), - ); + logger.i( + 'after withdrawal ETH execute => transactionHash: $transactionHash', + ); + emit(state.success(transactionHash: transactionHash)); + } - logger.i( - 'sending from: $sender to : ${state.withdrawalAddress},etherAmountInWei: ${EtherAmount.inWei(BigInt.from(amount))}', - ); + int transactionAttemptCount = 0; - logger.i( - 'after withdrawal ETH execute => transactionHash: $transactionHash', - ); - emit(state.success(transactionHash: transactionHash)); + void resetTransactionAttemptCount() { + transactionAttemptCount = 0; + } + + Future sendContractInvocationOperation() async { + try { + transactionAttemptCount++; + logger.i('attempt $transactionAttemptCount'); + emit(state.loading()); + + if (manageNetworkCubit.state.network is TezosNetwork) { + await _sendContractInvocationOperationTezos( + tokenAmount: double.parse(state.totalAmount), + selectedAccountSecretKey: state.selectedAccountSecretKey, + token: state.selectedToken, + rpcNodeUrl: rpcNodeUrlForTransaction, + ); + } else { + final selectedEthereumNetwork = manageNetworkCubit.state.network; + + final chainRpcUrl = await fetchRpcUrl(manageNetworkCubit.state.network); + + await _sendContractInvocationOperationEVM( + tokenAmount: state.totalAmount, + selectedAccountSecretKey: state.selectedAccountSecretKey, + token: state.selectedToken, + chainId: (selectedEthereumNetwork as EthereumNetwork).chainId, + chainRpcUrl: chainRpcUrl, + rpcUrl: chainRpcUrl, + ); + + resetTransactionAttemptCount(); + } } catch (e, s) { + if (transactionAttemptCount < 3) { + await Future.delayed(const Duration(milliseconds: 500)); + await sendContractInvocationOperation(); + return; + } + resetTransactionAttemptCount(); logger.e( 'error after withdrawal execute: e: $e, stack: $s', error: e, @@ -399,47 +473,6 @@ class ConfirmTokenTransactionCubit extends Cubit { } } - Future sendContractInvocationOperation() async { - try { - emit(state.loading()); - - if (manageNetworkCubit.state.network is TezosNetwork) { - await _sendContractInvocationOperationTezos( - tokenAmount: state.totalAmount, - selectedAccountSecretKey: state.selectedAccountSecretKey, - token: state.selectedToken, - rpcNodeUrl: rpcNodeUrlForTransaction, - ); - } else { - final selectedEthereumNetwork = manageNetworkCubit.state.network; - - await dotenv.load(); - final infuraApiKey = dotenv.get('INFURA_API_KEY'); - final ethRpcUrl = Urls.infuraBaseUrl + infuraApiKey; - - String chainRpcUrl = ethRpcUrl; - if (selectedEthereumNetwork is PolygonNetwork || - selectedEthereumNetwork is BinanceNetwork || - selectedEthereumNetwork is FantomNetwork) { - chainRpcUrl = selectedEthereumNetwork.rpcNodeUrl as String; - } else { - chainRpcUrl = ethRpcUrl; - } - await _sendContractInvocationOperationEthereum( - tokenAmount: state.totalAmount, - selectedAccountSecretKey: state.selectedAccountSecretKey, - token: state.selectedToken, - chainId: (selectedEthereumNetwork as EthereumNetwork).chainId, - chainRpcUrl: chainRpcUrl, - ethRpcUrl: ethRpcUrl, - ); - } - } catch (e, s) { - logger.e('e: $e s: $s'); - rethrow; - } - } - Future _sendContractInvocationOperationTezos({ required double tokenAmount, required String selectedAccountSecretKey, @@ -527,31 +560,25 @@ class ConfirmTokenTransactionCubit extends Cubit { emit(state.success()); } } catch (e, s) { - emit( - state.error( - messageHandler: ResponseMessage( - message: ResponseString - .RESPONSE_STRING_SOMETHING_WENT_WRONG_TRY_AGAIN_LATER, - ), - ), - ); getLogger(runtimeType.toString()) .e('error in transferOperation , e: $e, s: $s'); + + rethrow; } } - Future _sendContractInvocationOperationEthereum({ - required double tokenAmount, + Future _sendContractInvocationOperationEVM({ + required String tokenAmount, required String selectedAccountSecretKey, required TokenModel token, required int chainId, required String chainRpcUrl, - required String ethRpcUrl, + required String rpcUrl, }) async { try { - final ethBalance = await MWeb3Client.getETHBalance( + final ethBalance = await MWeb3Client.getEVMBalance( secretKey: state.selectedAccountSecretKey, - rpcUrl: ethRpcUrl, + rpcUrl: rpcUrl, ); if ((state.networkFee?.fee ?? 0) > ethBalance) { @@ -565,10 +592,7 @@ class ConfirmTokenTransactionCubit extends Cubit { return; } - if (token.symbol == 'ETH' || - token.symbol == 'MATIC' || - token.symbol == 'FTM' || - token.symbol == 'BNB') { + if (token.symbol.isEVM) { await _withdrawEthereumBaseTokenByChainId( tokenAmount: tokenAmount, selectedAccountSecretKey: selectedAccountSecretKey, @@ -581,7 +605,7 @@ class ConfirmTokenTransactionCubit extends Cubit { //final rpcUrl = manageNetworkCubit.state.network.rpcNodeUrl; //final rpcUrl = await web3RpcMainnetInfuraURL(); - final amount = tokenAmount * + final amount = double.parse(tokenAmount) * double.parse( 1.toStringAsFixed(int.parse(token.decimals)).replaceAll('.', ''), ); @@ -608,28 +632,9 @@ class ConfirmTokenTransactionCubit extends Cubit { ); } } catch (e, s) { - if (e is RPCError) { - emit( - state.copyWith( - status: AppStatus.error, - message: StateMessage.error( - stringMessage: e.message, - showDialog: true, - ), - ), - ); - } else { - emit( - state.error( - messageHandler: ResponseMessage( - message: ResponseString - .RESPONSE_STRING_SOMETHING_WENT_WRONG_TRY_AGAIN_LATER, - ), - ), - ); - } getLogger(runtimeType.toString()) .e('error in transferOperation , e: $e, s: $s'); + rethrow; } } diff --git a/lib/dashboard/home/tab_bar/tokens/confirm_token_transaction/cubit/confirm_token_transaction_state.dart b/lib/dashboard/home/tab_bar/tokens/confirm_token_transaction/cubit/confirm_token_transaction_state.dart index 54600a5ed..5db3dba9e 100644 --- a/lib/dashboard/home/tab_bar/tokens/confirm_token_transaction/cubit/confirm_token_transaction_state.dart +++ b/lib/dashboard/home/tab_bar/tokens/confirm_token_transaction/cubit/confirm_token_transaction_state.dart @@ -21,8 +21,8 @@ class ConfirmTokenTransactionState extends Equatable { final AppStatus status; final StateMessage? message; final String? transactionHash; - final double tokenAmount; - final double totalAmount; + final String tokenAmount; + final String totalAmount; final TokenModel selectedToken; final String selectedAccountSecretKey; final OperationsList? operationsList; @@ -71,8 +71,8 @@ class ConfirmTokenTransactionState extends Equatable { AppStatus? status, StateMessage? message, String? transactionHash, - double? tokenAmount, - double? totalAmount, + String? tokenAmount, + String? totalAmount, TokenModel? selectedToken, String? selectedAccountSecretKey, OperationsList? operationsList, diff --git a/lib/dashboard/home/tab_bar/tokens/confirm_token_transaction/view/confirm_token_transaction_page.dart b/lib/dashboard/home/tab_bar/tokens/confirm_token_transaction/view/confirm_token_transaction_page.dart index a8ff2b9a2..095b91daa 100644 --- a/lib/dashboard/home/tab_bar/tokens/confirm_token_transaction/view/confirm_token_transaction_page.dart +++ b/lib/dashboard/home/tab_bar/tokens/confirm_token_transaction/view/confirm_token_transaction_page.dart @@ -3,6 +3,7 @@ import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/l10n/l10n.dart'; import 'package:altme/theme/theme.dart'; import 'package:altme/wallet/cubit/wallet_cubit.dart'; +import 'package:decimal/decimal.dart'; import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -21,7 +22,7 @@ class ConfirmTokenTransactionPage extends StatelessWidget { static Route route({ required TokenModel selectedToken, required String withdrawalAddress, - required double amount, + required String amount, bool isNFT = false, }) { return MaterialPageRoute( @@ -37,7 +38,7 @@ class ConfirmTokenTransactionPage extends StatelessWidget { final TokenModel selectedToken; final String withdrawalAddress; - final double amount; + final String amount; final bool isNFT; @override @@ -45,6 +46,7 @@ class ConfirmTokenTransactionPage extends StatelessWidget { return BlocProvider( create: (_) => ConfirmTokenTransactionCubit( manageNetworkCubit: context.read(), + walletCubit: context.read(), client: DioClient( secureStorageProvider: getSecureStorage, dio: Dio(), @@ -80,7 +82,7 @@ class ConfirmWithdrawalView extends StatefulWidget { final TokenModel selectedToken; final String withdrawalAddress; - final double amount; + final String amount; final bool isNFT; @override @@ -128,7 +130,7 @@ class _ConfirmWithdrawalViewState extends State { } if (state.status == AppStatus.success) { final amountAndSymbol = - '''${widget.isNFT ? widget.amount.toInt() : state.totalAmount.toStringAsFixed(getDecimalsToShow(state.totalAmount)).formatNumber()} ${widget.isNFT ? '${widget.selectedToken.symbol} #${widget.selectedToken.tokenId}' : widget.selectedToken.symbol}'''; + '''${widget.isNFT ? Decimal.parse(widget.amount).toBigInt() : double.parse(state.totalAmount).decimalNumber(getDecimalsToShow(double.parse(state.totalAmount))).formatNumber} ${widget.isNFT ? '${widget.selectedToken.symbol} #${widget.selectedToken.tokenId}' : widget.selectedToken.symbol}'''; TransactionDoneDialog.show( context: context, amountAndSymbol: amountAndSymbol, @@ -140,6 +142,11 @@ class _ConfirmWithdrawalViewState extends State { } }, onDoneButtonClick: () { + if (widget.isNFT) { + context.read().fetchFromZero(); + } else { + context.read().fetchFromZero(); + } Navigator.popUntil( context, (route) => route.settings.name == AltMeStrings.dashBoardPage, @@ -150,7 +157,7 @@ class _ConfirmWithdrawalViewState extends State { }, builder: (context, state) { final amountAndSymbol = - '''${widget.isNFT ? widget.amount.toInt() : state.tokenAmount.toStringAsFixed(getDecimalsToShow(state.tokenAmount)).formatNumber()} ${widget.isNFT ? '${widget.selectedToken.symbol} #${widget.selectedToken.tokenId}' : widget.selectedToken.symbol}'''; + '''${widget.isNFT ? Decimal.parse(widget.amount).toBigInt() : double.parse(state.tokenAmount).decimalNumber(18).formatNumber} ${widget.isNFT ? '${widget.selectedToken.symbol} #${widget.selectedToken.tokenId}' : widget.selectedToken.symbol}'''; return BasePage( scrollView: false, title: l10n.confirm, @@ -165,17 +172,13 @@ class _ConfirmWithdrawalViewState extends State { crossAxisAlignment: CrossAxisAlignment.center, mainAxisSize: MainAxisSize.max, children: [ - const SizedBox( - height: Sizes.spaceSmall, - ), + const SizedBox(height: Sizes.spaceSmall), Text( l10n.amount, textAlign: TextAlign.center, style: Theme.of(context).textTheme.titleMedium, ), - const SizedBox( - height: Sizes.spaceSmall, - ), + const SizedBox(height: Sizes.spaceSmall), MyText( amountAndSymbol, textAlign: TextAlign.center, diff --git a/lib/dashboard/home/tab_bar/tokens/confirm_token_transaction/widgets/confirm_transaction_details_card.dart b/lib/dashboard/home/tab_bar/tokens/confirm_token_transaction/widgets/confirm_transaction_details_card.dart index 5faa3d00e..be508ceb1 100644 --- a/lib/dashboard/home/tab_bar/tokens/confirm_token_transaction/widgets/confirm_transaction_details_card.dart +++ b/lib/dashboard/home/tab_bar/tokens/confirm_token_transaction/widgets/confirm_transaction_details_card.dart @@ -2,6 +2,7 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/home/tab_bar/tokens/tokens.dart'; import 'package:altme/l10n/l10n.dart'; import 'package:altme/theme/theme.dart'; +import 'package:decimal/decimal.dart'; import 'package:flutter/material.dart'; class ConfirmTransactionDetailsCard extends StatelessWidget { @@ -17,14 +18,14 @@ class ConfirmTransactionDetailsCard extends StatelessWidget { this.isNFT = false, }); - final double amount; + final String amount; final double tokenUSDRate; final String symbol; final NetworkFeeModel? networkFee; final List? networkFees; final VoidCallback? onEditButtonPressed; final bool isNFT; - final double grandTotal; + final String grandTotal; @override Widget build(BuildContext context) { @@ -48,15 +49,15 @@ class ConfirmTransactionDetailsCard extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.end, children: [ Text( - '''${isNFT ? grandTotal.toInt() : grandTotal.toStringAsFixed(getDecimalsToShow(grandTotal)).formatNumber()} $symbol''', + '''${isNFT ? Decimal.parse(grandTotal).toBigInt() : double.parse(grandTotal).decimalNumber(getDecimalsToShow(double.parse(grandTotal))).formatNumber} $symbol''', style: Theme.of(context).textTheme.bodySmall, ), if (tokenUSDRate > 0) Text( r'$' + - (grandTotal * tokenUSDRate) - .toStringAsFixed(2) - .formatNumber(), + (double.parse(grandTotal) * tokenUSDRate) + .decimalNumber(2) + .formatNumber, style: Theme.of(context).textTheme.bodySmall2, ), ], @@ -83,15 +84,13 @@ class ConfirmTransactionDetailsCard extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.end, children: [ Text( - '''${networkFee!.fee.toStringAsFixed(6).formatNumber()} ${networkFee!.tokenSymbol}''', + '''${networkFee!.fee.decimalNumber(6).formatNumber} ${networkFee!.tokenSymbol}''', style: Theme.of(context).textTheme.bodySmall, ), if (tokenUSDRate > 0 && networkFee?.tokenSymbol == symbol) Text( r'$' + - networkFee!.feeInUSD - .toStringAsFixed(2) - .formatNumber(), + networkFee!.feeInUSD.decimalNumber(2).formatNumber, style: Theme.of(context).textTheme.bodySmall2, ), ], @@ -111,15 +110,15 @@ class ConfirmTransactionDetailsCard extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.end, children: [ Text( - '''${isNFT ? amount.toInt() : amount.toStringAsFixed(getDecimalsToShow(amount)).formatNumber()} $symbol''', + '''${isNFT ? Decimal.parse(amount).toBigInt() : double.parse(amount).decimalNumber(getDecimalsToShow(double.parse(amount))).formatNumber} $symbol''', style: Theme.of(context).textTheme.bodySmall, ), if (tokenUSDRate > 0) Text( r'$' + - (amount * tokenUSDRate) - .toStringAsFixed(2) - .formatNumber(), + (double.parse(amount) * tokenUSDRate) + .decimalNumber(2) + .formatNumber, style: Theme.of(context).textTheme.bodySmall2, ), ], diff --git a/lib/dashboard/home/tab_bar/tokens/insert_withdrawal_amount/cubit/insert_withdrawal_page_cubit.dart b/lib/dashboard/home/tab_bar/tokens/insert_withdrawal_amount/cubit/insert_withdrawal_page_cubit.dart index 93136bacf..4b0cb5a1f 100644 --- a/lib/dashboard/home/tab_bar/tokens/insert_withdrawal_amount/cubit/insert_withdrawal_page_cubit.dart +++ b/lib/dashboard/home/tab_bar/tokens/insert_withdrawal_amount/cubit/insert_withdrawal_page_cubit.dart @@ -1,6 +1,7 @@ import 'package:altme/app/logger/logger.dart'; import 'package:altme/dashboard/home/tab_bar/tokens/token_page/models/token_model.dart'; import 'package:bloc/bloc.dart'; +import 'package:decimal/decimal.dart'; import 'package:equatable/equatable.dart'; import 'package:json_annotation/json_annotation.dart'; @@ -12,21 +13,20 @@ class InsertWithdrawalPageCubit extends Cubit { InsertWithdrawalPageCubit({ required this.defaultSelectedToken, }) : super( - InsertWithdrawalPageState( - selectedToken: defaultSelectedToken, - ), + InsertWithdrawalPageState(selectedToken: defaultSelectedToken), ); final TokenModel defaultSelectedToken; final log = getLogger('InsertWithdrawalPageCubit'); - void setAmount({required double amount}) { + void setAmount({required String amount}) { emit( state.copyWith( amount: amount, - isValidWithdrawal: amount > 0 && - amount <= state.selectedToken.calculatedBalanceInDouble, + isValidWithdrawal: double.parse(amount) > 0 && + Decimal.parse(amount) <= + Decimal.parse(state.selectedToken.calculatedBalance), ), ); } @@ -35,8 +35,9 @@ class InsertWithdrawalPageCubit extends Cubit { emit( state.copyWith( selectedToken: selectedToken, - isValidWithdrawal: state.amount > 0 && - state.amount <= selectedToken.calculatedBalanceInDouble, + isValidWithdrawal: double.parse(state.amount) > 0 && + Decimal.parse(state.amount) <= + Decimal.parse(state.selectedToken.calculatedBalance), ), ); } diff --git a/lib/dashboard/home/tab_bar/tokens/insert_withdrawal_amount/cubit/insert_withdrawal_page_state.dart b/lib/dashboard/home/tab_bar/tokens/insert_withdrawal_amount/cubit/insert_withdrawal_page_state.dart index b64c468bd..96da9cdff 100644 --- a/lib/dashboard/home/tab_bar/tokens/insert_withdrawal_amount/cubit/insert_withdrawal_page_state.dart +++ b/lib/dashboard/home/tab_bar/tokens/insert_withdrawal_amount/cubit/insert_withdrawal_page_state.dart @@ -13,7 +13,7 @@ class InsertWithdrawalPageState extends Equatable { standard: 'fa1.2', decimalsToShow: 2, ), - this.amount = 0.0, + this.amount = '0', this.isValidWithdrawal = false, }); @@ -21,11 +21,11 @@ class InsertWithdrawalPageState extends Equatable { _$InsertWithdrawalPageStateFromJson(json); final TokenModel selectedToken; - final double amount; + final String amount; final bool isValidWithdrawal; InsertWithdrawalPageState copyWith({ - double? amount, + String? amount, bool? isValidWithdrawal, TokenModel? selectedToken, }) { diff --git a/lib/dashboard/home/tab_bar/tokens/insert_withdrawal_amount/cubit/token_amount_calculator_cubit.dart b/lib/dashboard/home/tab_bar/tokens/insert_withdrawal_amount/cubit/token_amount_calculator_cubit.dart index bb9333344..9a56cbf61 100644 --- a/lib/dashboard/home/tab_bar/tokens/insert_withdrawal_amount/cubit/token_amount_calculator_cubit.dart +++ b/lib/dashboard/home/tab_bar/tokens/insert_withdrawal_amount/cubit/token_amount_calculator_cubit.dart @@ -1,6 +1,7 @@ import 'package:altme/app/logger/logger.dart'; import 'package:altme/dashboard/dashboard.dart'; import 'package:bloc/bloc.dart'; +import 'package:decimal/decimal.dart'; import 'package:equatable/equatable.dart'; import 'package:json_annotation/json_annotation.dart'; @@ -19,17 +20,22 @@ class TokenAmountCalculatorCubit extends Cubit { required String amount, required TokenModel selectedToken, }) { - double validAmount = 0; - double insertedAmount = 0; + Decimal validAmount = Decimal.parse('0'); + String insertedAmount = ''; try { - if (amount.isEmpty || amount == '0' || amount == '0.0') { - insertedAmount = 0; - validAmount = 0; + if (amount.isEmpty || + amount == '0' || + amount == '0.0' || + amount == '00') { + validAmount = Decimal.parse('0'); + insertedAmount = ''; } else { - insertedAmount = double.parse(amount.replaceAll(',', '')); + insertedAmount = amount.replaceAll(',', ''); final bool isValid = isValidateAmount(amount: amount, selectedToken: selectedToken); - validAmount = isValid ? double.parse(amount.replaceAll(',', '')) : 0.0; + validAmount = isValid + ? Decimal.parse(amount.replaceAll(',', '')) + : Decimal.parse('0'); } } catch (e, s) { getLogger(runtimeType.toString()) @@ -38,13 +44,12 @@ class TokenAmountCalculatorCubit extends Cubit { emit( state.copyWith( - amount: amount, - validAmount: validAmount, + validAmount: validAmount.toString(), insertedAmount: insertedAmount, ), ); - insertWithdrawalPageCubit.setAmount(amount: validAmount); + insertWithdrawalPageCubit.setAmount(amount: validAmount.toString()); } bool isValidateAmount({ @@ -53,11 +58,9 @@ class TokenAmountCalculatorCubit extends Cubit { }) { if (amount == null) return false; try { - final insertedAmount = double.parse(amount.replaceAll(',', '')); - if (insertedAmount <= 0.0) return false; - final maxAmount = double.parse( - selectedToken.calculatedBalance.replaceAll(',', ''), - ); + final insertedAmount = Decimal.parse(amount.replaceAll(',', '')); + if (insertedAmount <= Decimal.parse('0.0')) return false; + final maxAmount = Decimal.parse(selectedToken.calculatedBalance); if (insertedAmount > maxAmount) { return false; } else { diff --git a/lib/dashboard/home/tab_bar/tokens/insert_withdrawal_amount/cubit/token_amount_calculator_state.dart b/lib/dashboard/home/tab_bar/tokens/insert_withdrawal_amount/cubit/token_amount_calculator_state.dart index 597a8135c..e51fcc083 100644 --- a/lib/dashboard/home/tab_bar/tokens/insert_withdrawal_amount/cubit/token_amount_calculator_state.dart +++ b/lib/dashboard/home/tab_bar/tokens/insert_withdrawal_amount/cubit/token_amount_calculator_state.dart @@ -3,26 +3,21 @@ part of 'token_amount_calculator_cubit.dart'; @JsonSerializable() class TokenAmountCalculatorState extends Equatable { const TokenAmountCalculatorState({ - this.amount = '', - this.validAmount = 0.0, - this.insertedAmount = 0.0, + this.validAmount = '0', + this.insertedAmount = '', }); factory TokenAmountCalculatorState.fromJson(Map json) => _$TokenAmountCalculatorStateFromJson(json); - final String amount; - final double validAmount; - final double insertedAmount; + final String validAmount; + final String insertedAmount; TokenAmountCalculatorState copyWith({ - String? amount, - double? validAmount, - double? insertedAmount, - TokenModel? selectedToken, + String? validAmount, + String? insertedAmount, }) { return TokenAmountCalculatorState( - amount: amount ?? this.amount, insertedAmount: insertedAmount ?? this.insertedAmount, validAmount: validAmount ?? this.validAmount, ); @@ -32,7 +27,6 @@ class TokenAmountCalculatorState extends Equatable { @override List get props => [ - amount, validAmount, insertedAmount, ]; diff --git a/lib/dashboard/home/tab_bar/tokens/insert_withdrawal_amount/view/insert_withdrawal_amount_page.dart b/lib/dashboard/home/tab_bar/tokens/insert_withdrawal_amount/view/insert_withdrawal_amount_page.dart index aad89a4bb..3fb08304c 100644 --- a/lib/dashboard/home/tab_bar/tokens/insert_withdrawal_amount/view/insert_withdrawal_amount_page.dart +++ b/lib/dashboard/home/tab_bar/tokens/insert_withdrawal_amount/view/insert_withdrawal_amount_page.dart @@ -78,9 +78,7 @@ class _InsertWithdrawalAmountViewState textAlign: TextAlign.center, style: Theme.of(context).textTheme.titleLarge, ), - const SizedBox( - height: Sizes.spaceSmall, - ), + const SizedBox(height: Sizes.spaceSmall), TokenSelectBoxView(selectedToken: state.selectedToken), TokenAmountCalculatorView(selectedToken: state.selectedToken), ], diff --git a/lib/dashboard/home/tab_bar/tokens/insert_withdrawal_amount/widgets/token_amount_calculator.dart b/lib/dashboard/home/tab_bar/tokens/insert_withdrawal_amount/widgets/token_amount_calculator.dart index b034db299..cac5a12f3 100644 --- a/lib/dashboard/home/tab_bar/tokens/insert_withdrawal_amount/widgets/token_amount_calculator.dart +++ b/lib/dashboard/home/tab_bar/tokens/insert_withdrawal_amount/widgets/token_amount_calculator.dart @@ -85,7 +85,7 @@ class _TokenAmountCalculatorPageState extends State { void _setAmountControllerText(String text) { // no need to format when text end with . - amountController.text = text.endsWith('.') ? text : text.formatNumber(); + amountController.text = text.endsWith('.') ? text : text.formatNumber; amountController.selection = TextSelection.fromPosition( TextPosition(offset: amountController.text.length), ); @@ -104,8 +104,8 @@ class _TokenAmountCalculatorPageState extends State { BlocBuilder( builder: (context, state) { getLogger('_setAmountControllerText') - .i('amount builder: ${state.amount}'); - _setAmountControllerText(state.amount); + .i('amount builder: ${state.insertedAmount}'); + _setAmountControllerText(state.insertedAmount); return Column( children: [ Form( @@ -168,14 +168,17 @@ class _TokenAmountCalculatorPageState extends State { ), ), UsdValueText( - usdValue: state.insertedAmount * - widget.selectedToken.tokenUSDPrice, + usdValue: state.insertedAmount.isEmpty + ? 0 + : double.parse(state.insertedAmount) * + widget.selectedToken.tokenUSDPrice, ), MaxButton( onTap: () { _setAmountControllerText( widget.selectedToken.calculatedBalance, ); + context.read().setAmount( amount: amountController.text, selectedToken: widget.selectedToken, diff --git a/lib/dashboard/home/tab_bar/tokens/insert_withdrawal_amount/widgets/token_select_box.dart b/lib/dashboard/home/tab_bar/tokens/insert_withdrawal_amount/widgets/token_select_box.dart index 332681de9..6a91436a0 100644 --- a/lib/dashboard/home/tab_bar/tokens/insert_withdrawal_amount/widgets/token_select_box.dart +++ b/lib/dashboard/home/tab_bar/tokens/insert_withdrawal_amount/widgets/token_select_box.dart @@ -119,9 +119,7 @@ class _TokenSelectBoxItem extends StatelessWidget { size: Sizes.icon, color: Theme.of(context).colorScheme.inversePrimary, ), - const Spacer( - flex: 1, - ), + const Spacer(flex: 1), MyText( tokenModel.calculatedBalance, minFontSize: 10, @@ -130,6 +128,7 @@ class _TokenSelectBoxItem extends StatelessWidget { style: Theme.of(context).textTheme.bodySmall, overflow: TextOverflow.ellipsis, ), + const SizedBox(width: 5), MyText( tokenModel.symbol, minFontSize: 10, @@ -143,8 +142,8 @@ class _TokenSelectBoxItem extends StatelessWidget { MyText( r'$' + selectedToken.balanceInUSD - .toStringAsFixed(2) - .formatNumber(), + .decimalNumber(2) + .formatNumber, style: Theme.of(context).textTheme.bodySmall?.copyWith( color: Theme.of(context).colorScheme.greyText, ), diff --git a/lib/dashboard/home/tab_bar/tokens/insert_withdrawal_amount/widgets/usd_value_text.dart b/lib/dashboard/home/tab_bar/tokens/insert_withdrawal_amount/widgets/usd_value_text.dart index 48de92fa8..e5d8cc064 100644 --- a/lib/dashboard/home/tab_bar/tokens/insert_withdrawal_amount/widgets/usd_value_text.dart +++ b/lib/dashboard/home/tab_bar/tokens/insert_withdrawal_amount/widgets/usd_value_text.dart @@ -24,7 +24,7 @@ class UsdValueText extends StatelessWidget { width: Sizes.spaceXSmall, ), Text( - r'$' + usdValue.toStringAsFixed(2).formatNumber(), + r'$' + usdValue.decimalNumber(2).formatNumber, style: Theme.of(context).textTheme.bodySmall, ), ], diff --git a/lib/dashboard/home/tab_bar/tokens/send_receive_home/view/send_receive_home_page.dart b/lib/dashboard/home/tab_bar/tokens/send_receive_home/view/send_receive_home_page.dart index 66da8feb0..950f1c79c 100644 --- a/lib/dashboard/home/tab_bar/tokens/send_receive_home/view/send_receive_home_page.dart +++ b/lib/dashboard/home/tab_bar/tokens/send_receive_home/view/send_receive_home_page.dart @@ -151,10 +151,10 @@ class _SendReceiveHomePageView extends StatelessWidget { children: [ MyText( state.selectedToken.calculatedBalanceInDouble - .toStringAsFixed( + .decimalNumber( state.selectedToken.decimalsToShow, ) - .formatNumber(), + .formatNumber, style: Theme.of(context).textTheme.headlineMedium, maxLength: 12, ), @@ -171,8 +171,8 @@ class _SendReceiveHomePageView extends StatelessWidget { MyText( r'$' + state.selectedToken.balanceInUSD - .toStringAsFixed(2) - .formatNumber(), + .decimalNumber(2) + .formatNumber, style: Theme.of(context).textTheme.normal, ), const SizedBox( diff --git a/lib/dashboard/home/tab_bar/tokens/send_receive_home/widgets/transaction_item.dart b/lib/dashboard/home/tab_bar/tokens/send_receive_home/widgets/transaction_item.dart index 39493822d..2c9dd5511 100644 --- a/lib/dashboard/home/tab_bar/tokens/send_receive_home/widgets/transaction_item.dart +++ b/lib/dashboard/home/tab_bar/tokens/send_receive_home/widgets/transaction_item.dart @@ -56,9 +56,7 @@ class TransactionItem extends StatelessWidget { ), Text( tokenUsdPrice != null - ? (tokenUsdPrice! * amount) - .toStringAsFixed(2) - .formatNumber() + + ? (tokenUsdPrice! * amount).decimalNumber(2).formatNumber + r'$' : r'$--.--', style: Theme.of(context).textTheme.bodySmall2, @@ -122,7 +120,7 @@ class TransactionItem extends StatelessWidget { ), Expanded( child: MyText( - '''${amount.toStringAsFixed(amount < 1 ? 5 : 2).formatNumber()} ''' + '''${amount.decimalNumber(amount < 1 ? 5 : 2).formatNumber} ''' '$symbol', minFontSize: 8, textAlign: TextAlign.end, diff --git a/lib/dashboard/home/tab_bar/tokens/token_page/widgets/token_item.dart b/lib/dashboard/home/tab_bar/tokens/token_page/widgets/token_item.dart index b3f216bb2..b99e19308 100644 --- a/lib/dashboard/home/tab_bar/tokens/token_page/widgets/token_item.dart +++ b/lib/dashboard/home/tab_bar/tokens/token_page/widgets/token_item.dart @@ -23,14 +23,16 @@ class TokenItem extends StatelessWidget { child: ListTile( contentPadding: EdgeInsets.zero, minVerticalPadding: 0, - leading: CachedImageFromNetwork( - token.iconUrl ?? '', - width: Sizes.tokenLogoSize, - height: Sizes.tokenLogoSize, - borderRadius: const BorderRadius.all( - Radius.circular(Sizes.tokenLogoSize), - ), - ), + leading: token.iconUrl == null + ? const CircleAvatar() + : CachedImageFromNetwork( + token.iconUrl!, + width: Sizes.tokenLogoSize, + height: Sizes.tokenLogoSize, + borderRadius: const BorderRadius.all( + Radius.circular(Sizes.tokenLogoSize), + ), + ), title: MyText( token.name.isEmpty ? token.symbol : token.name, style: Theme.of(context).textTheme.listTileTitle, @@ -49,7 +51,7 @@ class TokenItem extends StatelessWidget { child: MyText( isSecure ? '****' - : ('''${token.calculatedBalanceInDouble.toStringAsFixed(token.decimalsToShow).formatNumber()} ${token.symbol}'''), + : ('''${token.calculatedBalanceInDouble.decimalNumber(token.decimalsToShow).formatNumber} ${token.symbol}'''), style: Theme.of(context) .textTheme .listTileTitle @@ -61,7 +63,7 @@ class TokenItem extends StatelessWidget { isSecure ? '****' : (r'$' + - token.balanceInUSD.toStringAsFixed(2).formatNumber()), + token.balanceInUSD.decimalNumber(2).formatNumber), style: Theme.of(context).textTheme.listTileSubtitle, ), ), diff --git a/lib/dashboard/home/tab_bar/tokens/token_page/widgets/total_wallet_balance.dart b/lib/dashboard/home/tab_bar/tokens/token_page/widgets/total_wallet_balance.dart index e11c5f8e6..ee476a6c8 100644 --- a/lib/dashboard/home/tab_bar/tokens/token_page/widgets/total_wallet_balance.dart +++ b/lib/dashboard/home/tab_bar/tokens/token_page/widgets/total_wallet_balance.dart @@ -26,7 +26,7 @@ class TotalWalletBalance extends StatelessWidget { child: MyText( state.isSecure ? '****' - : '''${state.totalBalanceInUSD.toStringAsFixed(2).formatNumber()} \$''', + : '''${state.totalBalanceInUSD.decimalNumber(2).formatNumber} \$''', minFontSize: 8, style: Theme.of(context).textTheme.headlineMedium, ), diff --git a/lib/dashboard/profile/models/profile.dart b/lib/dashboard/profile/models/profile.dart index fbac64c53..b851d5588 100644 --- a/lib/dashboard/profile/models/profile.dart +++ b/lib/dashboard/profile/models/profile.dart @@ -50,7 +50,29 @@ class ProfileModel extends Equatable { blockchainOptions: BlockchainOptions.initial(), generalOptions: GeneralOptions.empty(), helpCenterOptions: HelpCenterOptions.initial(), - discoverCardsOptions: DiscoverCardsOptions.none(), + discoverCardsOptions: const DiscoverCardsOptions( + displayDefi: false, + displayHumanity: false, + displayHumanityJwt: false, + displayOver13: false, + displayOver15: false, + displayOver18: false, + displayOver18Jwt: false, + displayOver21: false, + displayOver50: false, + displayChainborn: false, + displayTezotopia: false, + displayVerifiableId: false, + displayVerifiableIdJwt: true, + displayOver65: false, + displayEmailPass: false, + displayEmailPassJwt: false, + displayPhonePass: false, + displayPhonePassJwt: false, + displayAgeRange: false, + displayGender: false, + displayExternalIssuer: [], + ), selfSovereignIdentityOptions: SelfSovereignIdentityOptions( displayManageDecentralizedId: true, customOidc4vcProfile: CustomOidc4VcProfile( diff --git a/lib/dashboard/profile/models/profile_setting.dart b/lib/dashboard/profile/models/profile_setting.dart index 009cfb116..1e15cf724 100644 --- a/lib/dashboard/profile/models/profile_setting.dart +++ b/lib/dashboard/profile/models/profile_setting.dart @@ -598,7 +598,12 @@ class CustomOidc4VcProfile extends Equatable { } else if (value == '12' || value == '13') { return OIDC4VCIDraftType.draft13; } else { - throw Exception(); + throw ResponseMessage( + data: { + 'error': 'invalid_format', + 'error_description': 'Error with oidc4vc draft type.', + }, + ); } } diff --git a/lib/dashboard/qr_code/qr_code_scan/cubit/qr_code_scan_cubit.dart b/lib/dashboard/qr_code/qr_code_scan/cubit/qr_code_scan_cubit.dart index 778af33fa..c69bce946 100644 --- a/lib/dashboard/qr_code/qr_code_scan/cubit/qr_code_scan_cubit.dart +++ b/lib/dashboard/qr_code/qr_code_scan/cubit/qr_code_scan_cubit.dart @@ -128,19 +128,7 @@ class QRCodeScanCubit extends Cubit { ); } catch (e, s) { log.e('Error -$e, stack: $s'); - if (e is MessageHandler) { - emitError(e); - } else { - var message = - ResponseString.RESPONSE_STRING_SOMETHING_WENT_WRONG_TRY_AGAIN_LATER; - - if (e.toString().startsWith('Exception: VERIFICATION_ISSUE')) { - message = ResponseString.RESPONSE_STRING_FAILED_TO_VERIFY_CREDENTIAL; - } else if (e.toString().startsWith('Exception: INIT_ISSUE')) { - message = ResponseString.RESPONSE_STRING_deviceIncompatibilityMessage; - } - emitError(ResponseMessage(message: message)); - } + emitError(e); } } @@ -1099,21 +1087,19 @@ class QRCodeScanCubit extends Cubit { ); } - if (publicKeyJwk != null) { - final VerificationType isVerified = await verifyEncodedData( - issuer: clientId, - jwtDecode: jwtDecode, - jwt: encodedData, - publicKeyJwk: publicKeyJwk, - ); + final VerificationType isVerified = await verifyEncodedData( + issuer: clientId, + jwtDecode: jwtDecode, + jwt: encodedData, + publicKeyJwk: publicKeyJwk, + ); - if (isVerified != VerificationType.verified) { - return emitError( - ResponseMessage( - message: ResponseString.RESPONSE_STRING_invalidRequest, - ), - ); - } + if (isVerified != VerificationType.verified) { + return emitError( + ResponseMessage( + message: ResponseString.RESPONSE_STRING_invalidRequest, + ), + ); } } @@ -1440,7 +1426,13 @@ class QRCodeScanCubit extends Cubit { openIdConfiguration: openIdConfiguration, ); } else { - throw Exception(); + throw ResponseMessage( + data: { + 'error': 'invalid_format', + 'error_description': 'Some issue with pre-authorization or ' + 'authorization flow parameters.', + }, + ); } } diff --git a/lib/dashboard/select_network_fee_bottom_sheet/widgets/select_network_fee_item.dart b/lib/dashboard/select_network_fee_bottom_sheet/widgets/select_network_fee_item.dart index 2b765b7c7..06f45ce5a 100644 --- a/lib/dashboard/select_network_fee_bottom_sheet/widgets/select_network_fee_item.dart +++ b/lib/dashboard/select_network_fee_bottom_sheet/widgets/select_network_fee_item.dart @@ -45,14 +45,14 @@ class SelectNetworkFeeItem extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.end, children: [ MyText( - '''${networkFeeModel.fee.toString().formatNumber()} ${networkFeeModel.tokenSymbol}''', + '''${networkFeeModel.fee.toString().formatNumber} ${networkFeeModel.tokenSymbol}''', style: Theme.of(context).textTheme.bodySmall, ), const SizedBox( height: Sizes.space2XSmall, ), MyText( - '''\$${networkFeeModel.feeInUSD == 0.0 ? '--.--' : networkFeeModel.feeInUSD.toStringAsFixed(4).formatNumber()}''', + '''\$${networkFeeModel.feeInUSD == 0.0 ? '--.--' : networkFeeModel.feeInUSD.decimalNumber(4).formatNumber}''', style: Theme.of(context).textTheme.bodySmall2, ), ], diff --git a/lib/enterprise/cubit/enterprise_cubit.dart b/lib/enterprise/cubit/enterprise_cubit.dart index 2f2de33ce..3220bdd68 100644 --- a/lib/enterprise/cubit/enterprise_cubit.dart +++ b/lib/enterprise/cubit/enterprise_cubit.dart @@ -205,7 +205,7 @@ class EnterpriseCubit extends Cubit { Future> getDataForRequest(String nonce) async { /// get private key final p256KeyForWallet = - await getWalletP256Key(profileCubit.secureStorageProvider); + await getWalletAttestationP256Key(profileCubit.secureStorageProvider); final privateKey = jsonDecode(p256KeyForWallet) as Map; final customOidc4vcProfile = profileCubit.state.model.profileSetting diff --git a/lib/l10n/arb/app_ca.arb b/lib/l10n/arb/app_ca.arb index 8e4940453..cd3922970 100644 --- a/lib/l10n/arb/app_ca.arb +++ b/lib/l10n/arb/app_ca.arb @@ -1020,28 +1020,26 @@ "profileName": "Nom del perfil", "companyName": "Nom de l’empresa", "configFileIdentifier": "Identificador de l’arxiu de config", - "clientCredentials": "Credencials del client", "updateYourWalletConfigNow": "Actualitzar ara config de cartera", "updateConfigurationNow": "Actualitza la configuració ara", "pleaseEnterYourEmailAndPasswordToUpdateYourOrganizationWalletConfiguration": "Indica el correu electrònic i la contrasenya per actualitzar la configuració de la cartera de la teva organització.", "congrats": "Felicitats!", "yourWalletConfigurationHasBeenSuccessfullyUpdated": "La configuració de la cartera s’ha actualitzat correctament", "continueString": "Continuar", - "walletProvider": "Proveïdor de cartera", - "clientTypeTitle" : "Tipus de Client OIDC4VCI", -"clientTypeSubtitle" : "Predeterminat: DID\nDesplaci per canviar el tipus de client", -"proofHeader" : "Capçalera de prova de possessió", -"proofHeaderSubtitle" : "Predeterminat: kid\nCanviar si es necessita JWK a la capçalera", -"theLdpFormatIsNotSupportedByThisDIDMethod": "El format LDP no és compatible amb aquest mètode DID", -"switchOffCryptoHolderBindingForThatDIDMethod": "Desactivar la vinculació del titular (o posseïdor) de criptoactius per a aquest mètode DID", -"thisTypeProofCannotBeUsedWithThisVCFormat": "Aquest tipus de prova no es pot fer servir amb aquest format de CV", -"enterprise" : "Organització/Empresa", -"oWFBaselineProfile": "Perfil de Línia Base d'OWF", -"defaultProfile" : "Predeterminat", -"blockchainCardsDiscoverTitle": "Obtenir prova de propietat de compte cripto", -"blockchainCardsDiscoverSubtitle" : "Obtenir prova de propietat de compte de criptoactius", -"successfullyAddedEnterpriseAccount": "Compte d'Organització/Empresa afegida amb èxit!", -"successfullyUpdatedEnterpriseAccount" : "Compte d'Organització/Empresa actualitzada amb èxit!", -"languageSelectorTitle": "catalan" + "walletProvider": "Proveïdor de cartera", + "clientTypeSubtitle" : "Predeterminat: DID\nDesplaci per canviar el tipus de client", + "proofHeader" : "Capçalera de prova de possessió", + "proofHeaderSubtitle" : "Predeterminat: kid\nCanviar si es necessita JWK a la capçalera", + "theLdpFormatIsNotSupportedByThisDIDMethod": "El format LDP no és compatible amb aquest mètode DID", + "switchOffCryptoHolderBindingForThatDIDMethod": "Desactivar la vinculació del titular (o posseïdor) de criptoactius per a aquest mètode DID", + "thisTypeProofCannotBeUsedWithThisVCFormat": "Aquest tipus de prova no es pot fer servir amb aquest format de CV", + "enterprise" : "Organització/Empresa", + "oWFBaselineProfile": "Perfil de Línia Base d'OWF", + "defaultProfile" : "Predeterminat", + "blockchainCardsDiscoverTitle": "Obtenir prova de propietat de compte cripto", + "blockchainCardsDiscoverSubtitle" : "Obtenir prova de propietat de compte de criptoactius", + "successfullyAddedEnterpriseAccount": "Compte d'Organització/Empresa afegida amb èxit!", + "successfullyUpdatedEnterpriseAccount" : "Compte d'Organització/Empresa actualitzada amb èxit!", + "languageSelectorTitle": "catalan" } \ No newline at end of file diff --git a/lib/l10n/arb/app_en.arb b/lib/l10n/arb/app_en.arb index f31701e58..978479f64 100644 --- a/lib/l10n/arb/app_en.arb +++ b/lib/l10n/arb/app_en.arb @@ -1010,8 +1010,7 @@ "organizationProfile": "Organization Profile", "profileName": "Profile Name", "companyName": "Company Name", - "configFileIdentifier": "Config file identifier", - "clientCredentials": "Client Credentials", + "configFileIdentifier": "Config file identifier", "updateYourWalletConfigNow": "Update your wallet config now", "updateConfigurationNow": "Update configuration now", "pleaseEnterYourEmailAndPasswordToUpdateYourOrganizationWalletConfiguration": "Please enter your email and password to update your organization wallet configuration", @@ -1019,7 +1018,6 @@ "yourWalletConfigurationHasBeenSuccessfullyUpdated": "Your wallet configuration has been successfully updated", "continueString": "Continue", "walletProvider": "Wallet Provider", - "clientTypeTitle": "OIDC4VCI Client Type", "clientTypeSubtitle": "Default: DID\nSwitch to change the client type", "proofHeader": "Proof of Possession Header", "proofHeaderSubtitle": "Default: kid\nSwitch if jwk is needed in header.", @@ -1056,5 +1054,8 @@ "statusListCachingSubTitle": "Default: On\nSwitch off to reload StatusList when needed", "statusList": "Status list", "statusListIndex": "Status list index", - "theWalletIsSuspended": "The wallet is suspended." + "theWalletIsSuspended": "The wallet is suspended.", + "clientTypeTitle": "Wallet Client_id Scheme", + "confidentialClient": "Confidential Client", + "jwkThumbprintP256Key": "JWK Thumbprint P-256 Key" } diff --git a/lib/l10n/arb/app_es.arb b/lib/l10n/arb/app_es.arb index 8405c2e9c..b6a45d984 100644 --- a/lib/l10n/arb/app_es.arb +++ b/lib/l10n/arb/app_es.arb @@ -1020,7 +1020,6 @@ "profileName": "Nombre de perfil", "companyName": "Nombre de la empresa", "configFileIdentifier": "Identificador de archivo de configuración", - "clientCredentials": "Credenciales de cliente", "updateYourWalletConfigNow": "Actualizar la configuración de cartera ahora", "updateConfigurationNow": "Actualizar la configuración ahora", "pleaseEnterYourEmailAndPasswordToUpdateYourOrganizationWalletConfiguration": "Escriba su email y contraseña para actualizar la configuración de cartera de su organización", @@ -1028,7 +1027,6 @@ "yourWalletConfigurationHasBeenSuccessfullyUpdated": "Configuración de cartera actualizada", "continueString": "Continuar", "walletProvider": "Proveedor de cartera", - "clientTypeTitle": "Tipo de Cliente OIDC4VCI", "clientTypeSubtitle" : "Predeterminado: DID\nDesplace para cambiar el tipo de cliente", "proofHeader" : "Encabezado de Prueba de Posesión", "proofHeaderSubtitle" : "Predeterminado: kid\nCambiar si necesita JWK en el encabezado.", diff --git a/lib/l10n/arb/app_fr.arb b/lib/l10n/arb/app_fr.arb index ed8527026..6af25f4cf 100644 --- a/lib/l10n/arb/app_fr.arb +++ b/lib/l10n/arb/app_fr.arb @@ -965,7 +965,6 @@ "jwkDecentralizedIDP256": "JWK d'Identité Décentralisée P-256", "defaultDid": "DID par Défaut", "selectOneOfTheDid": "Sélectionnez l'un des DIDs", - "clientTypeTitle": "Type de Client OIDC4VCI", "clientTypeSubtitle": "Par défaut : DID\nSwitch pour changer le type de client", "cryptographicHolderBinding": "Liaison du Titulaire Cryptographique", "cryptographicHolderBindingSubtitle": "Par défaut : On\nDisable cryptographic binding for claim based binding credentials.", @@ -1022,7 +1021,6 @@ "profileName": "Nom du profil", "companyName": "Nom de l'entreprise", "configFileIdentifier": "Identifiant du fichier de configuration", - "clientCredentials": "Informations d'Identification du Client", "updateYourWalletConfigNow": "Mettez à jour votre configuration maintenant", "updateConfigurationNow": "Mettre à jour la configuration maintenant", "pleaseEnterYourEmailAndPasswordToUpdateYourOrganizationWalletConfiguration": "Veuillez entrer votre email et votre mot de passe pour mettre à jour votre portefeuille", diff --git a/lib/l10n/untranslated.json b/lib/l10n/untranslated.json index 2a536b5cb..59c248ba9 100644 --- a/lib/l10n/untranslated.json +++ b/lib/l10n/untranslated.json @@ -23,7 +23,10 @@ "statusListCachingSubTitle", "statusList", "statusListIndex", - "theWalletIsSuspended" + "theWalletIsSuspended", + "clientTypeTitle", + "confidentialClient", + "jwkThumbprintP256Key" ], "es": [ @@ -50,7 +53,10 @@ "statusListCachingSubTitle", "statusList", "statusListIndex", - "theWalletIsSuspended" + "theWalletIsSuspended", + "clientTypeTitle", + "confidentialClient", + "jwkThumbprintP256Key" ], "fr": [ @@ -67,6 +73,9 @@ "statusListCachingSubTitle", "statusList", "statusListIndex", - "theWalletIsSuspended" + "theWalletIsSuspended", + "clientTypeTitle", + "confidentialClient", + "jwkThumbprintP256Key" ] } diff --git a/lib/oidc4vc/add_credential_data.dart b/lib/oidc4vc/add_credential_data.dart index 7b5309f17..bd724a908 100644 --- a/lib/oidc4vc/add_credential_data.dart +++ b/lib/oidc4vc/add_credential_data.dart @@ -38,7 +38,12 @@ Future addCredentialData({ final id = const Uuid().v4(); if (data is! Map) { - throw Exception(); + throw ResponseMessage( + data: { + 'error': 'invalid_format', + 'error_description': 'The format of credential data should be Map.', + }, + ); } final credentialModel = CredentialModel( diff --git a/lib/oidc4vc/add_oidc4vc_credential.dart b/lib/oidc4vc/add_oidc4vc_credential.dart index 61ac1c930..9f771e97b 100644 --- a/lib/oidc4vc/add_oidc4vc_credential.dart +++ b/lib/oidc4vc/add_oidc4vc_credential.dart @@ -125,7 +125,12 @@ Future addOIDC4VCCredential({ : jsonDecode(encodedCredentialFromOIDC4VC['credential'].toString()) as Map; } else { - throw Exception(); + throw ResponseMessage( + data: { + 'error': 'invalid_format', + 'error_description': 'The format of vc is incorrect.', + }, + ); } final Map newCredential = diff --git a/lib/oidc4vc/get_authorization_uri_for_issuer.dart b/lib/oidc4vc/get_authorization_uri_for_issuer.dart index 06e2ba77a..146133df2 100644 --- a/lib/oidc4vc/get_authorization_uri_for_issuer.dart +++ b/lib/oidc4vc/get_authorization_uri_for_issuer.dart @@ -110,7 +110,14 @@ Future getAuthorizationUriForIssuer({ final requestUri = response['request_uri']; - if (requestUri == null) throw Exception(); + if (requestUri == null) { + throw ResponseMessage( + data: { + 'error': 'invalid_format', + 'error_description': 'The request_uri should be provided.', + }, + ); + } final parameters = {'client_id': clientId, 'request_uri': requestUri}; diff --git a/lib/pin_code/view/pin_code_page.dart b/lib/pin_code/view/pin_code_page.dart index ba8a9d326..7cb0efa0d 100644 --- a/lib/pin_code/view/pin_code_page.dart +++ b/lib/pin_code/view/pin_code_page.dart @@ -110,7 +110,12 @@ class _PinCodeViewState extends State { Navigator.pop(context); widget.isValidCallback.call(); case WalletProtectionType.biometrics: - throw Exception(); + throw ResponseMessage( + data: { + 'error': 'invalid_format', + 'error_description': 'The biomertics is not supported.', + }, + ); case WalletProtectionType.FA2: final LocalAuthApi localAuthApi = LocalAuthApi(); final authenticated = await localAuthApi.authenticate( diff --git a/lib/polygon_id/cubit/polygon_id_cubit.dart b/lib/polygon_id/cubit/polygon_id_cubit.dart index 95710f687..458370e98 100644 --- a/lib/polygon_id/cubit/polygon_id_cubit.dart +++ b/lib/polygon_id/cubit/polygon_id_cubit.dart @@ -87,7 +87,7 @@ class PolygonIdCubit extends Cubit { currentNetwork = PolygonIdNetwork.PolygonMainnet; await polygonId.init( network: network, - web3Url: Parameters.INFURA_URL, + web3Url: Parameters.POLYGON_INFURA_URL, web3RdpUrl: Parameters.INFURA_RDP_URL, web3ApiKey: dotenv.get('INFURA_API_KEY'), idStateContract: Parameters.ID_STATE_CONTRACT_ADDR, @@ -123,7 +123,9 @@ class PolygonIdCubit extends Cubit { ); } catch (e) { emit(state.copyWith(status: AppStatus.error, isInitialised: false)); - throw Exception('INIT_ISSUE - $e'); + throw ResponseMessage( + message: ResponseString.RESPONSE_STRING_deviceIncompatibilityMessage, + ); } } @@ -145,7 +147,7 @@ class PolygonIdCubit extends Cubit { network = Parameters.POLYGON_MAIN_NETWORK; await polygonId.setEnv( network: Parameters.POLYGON_MAIN_NETWORK, - web3Url: Parameters.INFURA_URL, + web3Url: Parameters.POLYGON_INFURA_URL, web3RdpUrl: Parameters.INFURA_RDP_URL, web3ApiKey: dotenv.get('INFURA_API_KEY'), idStateContract: Parameters.ID_STATE_CONTRACT_ADDR, @@ -179,7 +181,13 @@ class PolygonIdCubit extends Cubit { ); } catch (e) { emit(state.copyWith(status: AppStatus.error)); - throw Exception('UPDATE_ISSUE - $e'); + + throw ResponseMessage( + data: { + 'error': 'invalid_request', + 'error_description': 'UPDATE_ISSUE - $e', + }, + ); } } @@ -348,7 +356,14 @@ class PolygonIdCubit extends Cubit { } if (claims.length != credentialManifests.length) { - throw Exception(); + throw ResponseMessage( + data: { + 'error': 'invalid_format', + 'error_description': + 'The claims length and crdential manifest length ' + "doesn't match.", + }, + ); } log.i('get claims'); diff --git a/lib/scan/cubit/scan_cubit.dart b/lib/scan/cubit/scan_cubit.dart index 219181b69..92e505ca6 100644 --- a/lib/scan/cubit/scan_cubit.dart +++ b/lib/scan/cubit/scan_cubit.dart @@ -89,7 +89,13 @@ class ScanCubit extends Cubit { if (isIDTokenOnly(responseType)) { /// verifier side (siopv2) with request uri as value - throw Exception(); + throw ResponseMessage( + data: { + 'error': 'invalid_request', + 'error_description': + 'The verifier side must not contain id_token only.', + }, + ); } else if (isVPTokenOnly(responseType)) { /// verifier side (oidc4vp) with request uri as value @@ -128,7 +134,13 @@ class ScanCubit extends Cubit { return; } else { - throw Exception(); + throw ResponseMessage( + data: { + 'error': 'invalid_request', + 'error_description': + 'The response type should contain id_token, vp_token or both.', + }, + ); } } else { /// If credential manifest exist we follow instructions to present @@ -567,7 +579,7 @@ class ScanCubit extends Cubit { did: '', // just added as it is required field mediaType: MediaType.basic, // just added as it is required field clientType: - ClientType.jwkThumbprint, // just added as it is required field + ClientType.p256JWKThumprint, // just added as it is required field proofHeaderType: customOidc4vcProfile.proofHeader, clientId: '', // just added as it is required field ); @@ -926,7 +938,13 @@ class ScanCubit extends Cubit { return vpToken; } else { - throw Exception(); + throw ResponseMessage( + data: { + 'error': 'invalid_format', + 'error_description': + 'Please present ldp_vc, jwt_vc, jwt_vc_json or vc+sd-jwt.', + }, + ); } } diff --git a/lib/selective_disclosure/selective_disclosure.dart b/lib/selective_disclosure/selective_disclosure.dart index a731505ea..456c8152e 100644 --- a/lib/selective_disclosure/selective_disclosure.dart +++ b/lib/selective_disclosure/selective_disclosure.dart @@ -1,5 +1,6 @@ import 'dart:convert'; +import 'package:altme/app/app.dart'; import 'package:altme/dashboard/home/tab_bar/credentials/models/credential_model/credential_model.dart'; import 'package:altme/selective_disclosure/selective_disclosure.dart'; import 'package:json_path/json_path.dart'; @@ -63,11 +64,23 @@ class SelectiveDisclosure { extractedValues[lisString[0].toString()] = lisString[1]; } else { - throw Exception(); + throw ResponseMessage( + data: { + 'error': 'invalid_format', + 'error_description': + 'The disclosure content should contain 2 or 3 elements.', + }, + ); } } } catch (e) { - throw Exception(); + throw ResponseMessage( + data: { + 'error': 'invalid_format', + 'error_description': + 'Something went wrong when extracting content from disclosure.', + }, + ); } } return extractedValues; diff --git a/lib/splash/bloclisteners/blocklisteners.dart b/lib/splash/bloclisteners/blocklisteners.dart index 41a5d238b..38076f3fa 100644 --- a/lib/splash/bloclisteners/blocklisteners.dart +++ b/lib/splash/bloclisteners/blocklisteners.dart @@ -587,18 +587,7 @@ final qrCodeBlocListener = BlocListener( ); } } catch (e) { - if (e.toString().startsWith('Exception: Openid-Configuration-Issue')) { - context.read().emitError( - ResponseMessage( - data: { - 'error': 'unsupported_format', - 'error_description': 'Openid configuration response issue.', - }, - ), - ); - } else { - context.read().emitError(e); - } + context.read().emitError(e); } }, ); diff --git a/packages/jwt_decode/lib/src/jwt_decode.dart b/packages/jwt_decode/lib/src/jwt_decode.dart index a52e650c4..2eafff5b8 100644 --- a/packages/jwt_decode/lib/src/jwt_decode.dart +++ b/packages/jwt_decode/lib/src/jwt_decode.dart @@ -8,14 +8,14 @@ class JWTDecode { Map parseJwt(String token) { final parts = token.split('.'); if (parts.length != 3) { - throw Exception('Invalid Token'); + throw Exception('INVALID_TOKEN'); } final payload = _decodeBase64(parts[1]); final dynamic payloadMap = json.decode(payload); if (payloadMap is! Map) { - throw Exception('Invalid Payload'); + throw Exception('INVALID_PAYLOAD'); } return payloadMap; } @@ -24,14 +24,14 @@ class JWTDecode { Map parseJwtHeader(String token) { final parts = token.split('.'); if (parts.length != 3) { - throw Exception('Invalid Token'); + throw Exception('INVALID_TOKEN'); } final header = _decodeBase64(parts[0]); final dynamic headerMap = json.decode(header); if (headerMap is! Map) { - throw Exception('Invalid Payload'); + throw Exception('INVALID_PAYLOAD'); } return headerMap; } @@ -42,7 +42,7 @@ class JWTDecode { final dynamic headerMap = json.decode(header); if (headerMap is! Map) { - throw Exception('Invalid Payload'); + throw Exception('INVALID_PAYLOAD'); } return headerMap; } diff --git a/packages/key_generator/lib/src/key_generator.dart b/packages/key_generator/lib/src/key_generator.dart index 6512b9f64..46f10a4ec 100644 --- a/packages/key_generator/lib/src/key_generator.dart +++ b/packages/key_generator/lib/src/key_generator.dart @@ -165,7 +165,7 @@ class KeyGenerator { return '0x$epk'; case AccountType.ssi: - throw Exception(); + throw Exception('SSI_ISSUE'); } } @@ -214,7 +214,7 @@ class KeyGenerator { return walletAddress.hex; case AccountType.ssi: - throw Exception(); + throw Exception('SSI_ISSUE'); } } @@ -237,7 +237,7 @@ class KeyGenerator { return walletAddress.hex; case AccountType.ssi: - throw Exception(); + throw Exception('SSI_ISSUE'); } } diff --git a/packages/oidc4vc/lib/src/client_type.dart b/packages/oidc4vc/lib/src/client_type.dart index 466a3352c..f313a04fc 100644 --- a/packages/oidc4vc/lib/src/client_type.dart +++ b/packages/oidc4vc/lib/src/client_type.dart @@ -2,7 +2,7 @@ import 'package:json_annotation/json_annotation.dart'; enum ClientType { @JsonValue('urn:ietf:params:oauth:jwk-thumbprint') - jwkThumbprint, + p256JWKThumprint, did, @@ -12,8 +12,8 @@ enum ClientType { extension ClientTypeX on ClientType { String get getTitle { switch (this) { - case ClientType.jwkThumbprint: - return 'JWK Thumbprint'; + case ClientType.p256JWKThumprint: + return 'P-256 JWK Thumbprint'; case ClientType.did: return 'DID'; case ClientType.confidential: diff --git a/packages/oidc4vc/lib/src/oidc4vc.dart b/packages/oidc4vc/lib/src/oidc4vc.dart index 2d58d7ac3..33679a15f 100644 --- a/packages/oidc4vc/lib/src/oidc4vc.dart +++ b/packages/oidc4vc/lib/src/oidc4vc.dart @@ -5,6 +5,7 @@ import 'dart:io'; import 'package:bip32/bip32.dart' as bip32; import 'package:bip39/bip39.dart' as bip393; +import 'package:bs58/bs58.dart'; import 'package:crypto/crypto.dart'; import 'package:dart_jsonwebtoken/dart_jsonwebtoken.dart'; import 'package:did_kit/did_kit.dart'; @@ -182,7 +183,7 @@ class OIDC4VC { return (authorizationEndpoint, authorizationRequestParemeters); } catch (e) { - throw Exception('Not a valid openid url to initiate issuance'); + throw Exception('NOT_A_VALID_OPENID_URL'); } } @@ -883,9 +884,20 @@ class OIDC4VC { .toList(); } - final value = data.first['publicKeyJwk']; + final method = data.first as Map; - return jsonDecode(jsonEncode(value)) as Map; + dynamic publicKeyJwk; + + if (method.containsKey('publicKeyJwk')) { + publicKeyJwk = method['publicKeyJwk']; + } else if (method.containsKey('publicKeyBase58')) { + publicKeyJwk = + publicKeyBase58ToPublicJwk(method['publicKeyBase58'].toString()); + } else { + throw Exception('PUBLICKEYJWK_EXTRACTION_ERROR'); + } + + return jsonDecode(jsonEncode(publicKeyJwk)) as Map; } } @@ -1506,7 +1518,7 @@ class OIDC4VC { var issAndSub = tokenParameters.thumbprint; switch (tokenParameters.clientType) { - case ClientType.jwkThumbprint: + case ClientType.p256JWKThumprint: issAndSub = tokenParameters.thumbprint; case ClientType.did: issAndSub = tokenParameters.did; @@ -1527,7 +1539,7 @@ class OIDC4VC { payload['nonce'] = tokenParameters.nonce!; } - if (tokenParameters.clientType == ClientType.jwkThumbprint) { + if (tokenParameters.clientType == ClientType.p256JWKThumprint) { payload['sub_jwk'] = tokenParameters.publicJWK; } @@ -1638,7 +1650,7 @@ class OIDC4VC { : response as Map; return OpenIdConfiguration.fromJson(data); } catch (e) { - throw Exception('Openid-Configuration-Issue'); + throw Exception('OPENID-CONFIGURATION-ISSUE'); } } @@ -1746,4 +1758,22 @@ class OIDC4VC { } } } + + Map publicKeyBase58ToPublicJwk(String publicKeyBase58) { + ///step 1 : change the publicKeyBase58 format from base58 to base64 : + ///decode base58 then encode in base64 urlsafe + + final pubKey = + base64UrlEncode(base58.decode(publicKeyBase58)).replaceAll('=', ''); + + ///step 2 : create the JWK for the "type": "Ed + ///25519VerificationKey2018", + ///it is a edDSA key + final jwk = { + 'crv': 'Ed25519', + 'kty': 'OKP', + 'x': pubKey, + }; + return jwk; + } } diff --git a/packages/oidc4vc/pubspec.yaml b/packages/oidc4vc/pubspec.yaml index af73b1a8c..a59f682f8 100644 --- a/packages/oidc4vc/pubspec.yaml +++ b/packages/oidc4vc/pubspec.yaml @@ -9,6 +9,7 @@ environment: dependencies: bip32: ^2.0.0 bip39: ^1.0.6 + bs58: ^1.0.2 credential_manifest: path: ../credential_manifest crypto: ^3.0.3 diff --git a/packages/oidc4vc/test/src/oidc4vc_test.dart b/packages/oidc4vc/test/src/oidc4vc_test.dart index 1ee77dbb3..16d13c8dd 100644 --- a/packages/oidc4vc/test/src/oidc4vc_test.dart +++ b/packages/oidc4vc/test/src/oidc4vc_test.dart @@ -191,6 +191,23 @@ void main() { }); }); + group('publicKeyBase58ToPublicJwk', () { + final oidc4vc = OIDC4VC(); + + const publicKeyBase58 = '2S73k5pn5umfnaW31qx6dXFndEn6SmWw7LpgSjNNC5BF'; + + final expectedPublicJWK = { + 'crv': 'Ed25519', + 'kty': 'OKP', + 'x': 'FUoLewH4w4-KdaPH2cjZbL--CKYxQRWR05Yd_bIbhQo', + }; + + test('convert publicKeyBase58 to PublicJwk', () { + final publicKey = oidc4vc.publicKeyBase58ToPublicJwk(publicKeyBase58); + expect(publicKey, expectedPublicJWK); + }); + }); + group('EBSI: getAuthorizationUriForIssuer', () { const issuer = 'https://talao.co/sandbox/ebsi/issuer/pcbrwbvrsi'; diff --git a/packages/polygonid/lib/src/polygonid.dart b/packages/polygonid/lib/src/polygonid.dart index 4f17a6fe8..b13f2fba4 100644 --- a/packages/polygonid/lib/src/polygonid.dart +++ b/packages/polygonid/lib/src/polygonid.dart @@ -133,7 +133,7 @@ class PolygonId { ); return identity; } else { - throw Exception('STH_WENT_WRONG - $e'); + throw Exception('ISSUE_WHILE_ADDING_IDENTITY'); } } } @@ -298,7 +298,7 @@ class PolygonId { return claimEntities; } catch (e) { - throw Exception(); + throw Exception('ISSUE_WHILE_GETTING_CLAIMS'); } } @@ -410,7 +410,7 @@ class PolygonId { return claimEntities; } catch (e) { - throw Exception(); + throw Exception('ISSUE_WHILE_RESTORING_CLAIMS'); } } @@ -458,7 +458,7 @@ class PolygonId { return claimEntities; } catch (e) { - throw Exception(); + throw Exception('ISSUE_WHILE_GETTING_CLAIMS'); } } diff --git a/packages/secure_storage/lib/src/secure_storage.dart b/packages/secure_storage/lib/src/secure_storage.dart index ec33df553..e11ddbc46 100644 --- a/packages/secure_storage/lib/src/secure_storage.dart +++ b/packages/secure_storage/lib/src/secure_storage.dart @@ -62,7 +62,7 @@ class SecureStorageProvider { if (_instance._storage == null) { // TODO(all): Explain user and give him // possibility to send issue report? - throw Exception('Secure Storage issue with this device'); + throw Exception('SECURE_STORAGE_ISSUE'); } return _instance; } diff --git a/pubspec.yaml b/pubspec.yaml index 37d01bbcc..32db785f0 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: altme description: AltMe Flutter App -version: 2.4.22+442 +version: 2.4.27+447 environment: sdk: ">=3.1.0 <4.0.0" @@ -33,6 +33,7 @@ dependencies: git: url: https://github.com/TalebRafiepour/Dartez.git ref: main + decimal: ^2.3.3 device_info_plus: ^9.0.1 device_preview: ^1.1.0 devicelocale: ^0.7.0