From 6fa1c5aa3602bc9bc729b3f0f16f6af8e5064064 Mon Sep 17 00:00:00 2001 From: hawkbee <49282360+hawkbee1@users.noreply.github.com> Date: Thu, 15 Aug 2024 16:25:38 +0200 Subject: [PATCH] October (#2838) * fix: Update account name instantly #2667 * linter update * feat: Add green color for card valid status #2764 * color adjustment * fix: Avoid double scan to get and present #2668 * fix: Fetch uri for image but not working and indication of deleted message #2771 * feat: Add all type of VCs #2779 * feat: Support all types presentaton wth VCFormat auto #2779 * feat: Update credential request for draft 13 - jwt_vc_json and ldp_vc #2796 * version update: 2.9.8+477 * Revert "version update: 2.9.8+477" This reverts commit 6e8265c4473da0850086996582916b51733b7249. * refactor: Update oidc4vc test * refactor: Try to display image #2771 * fix: Fix image and add photo viewer #2771 * refactor: Optimise code to load image smoothly side by side #2771 * feat: Display advanced security option in enterprise #2584 * feat: Update error responses #2573 * refactor: Remove long error message for now #2573 * refactor: Display advanced security options #2584 * refactor: Bug fix and optimise code * some update and package update * feat: Some bug fix * feat: Add some etherlink functionlities #2820 * version update: 2.10.4+486 * feat: Add empty etherlink when no tokens are available * version update : 2.10.5+487 * feat: Add etherlink balance and transfer * version update: 2.10.6+488 * refactor: Copy clipboard data correctly #2830 * refactor: Copy clipboard data correctly #2830 * refactor: Create crypto account VC for etherlink #2833 * feat: Set timeout of 10seconds for statulist http get #2824 * feat: Enable advanced settings for non-enterprise profile #2828 * refactor: Correct the backend provider url #2818 * feat: Display some vc based on crypto account did issuer #2680 * version update: 2.10.7+489 * fix: Advanced security option #2828 * feat: Fix wallet configuration failing issue #2835 * fix: Show crypto account for respective wallet address only #2833 * version update: 2.10.8+490 --------- Co-authored-by: Bibash Shrestha --- lib/app/shared/constants/parameters.dart | 81 +++++++ .../shared/extension/string_extension.dart | 25 +- .../helper_functions/helper_functions.dart | 58 ++++- .../blockchain_network/etherlink_network.dart | 8 +- .../cubit/credentials_helper_function.dart | 17 +- .../widgets/crypto_accont_item.dart | 4 +- .../widgets/manage_accounts_item.dart | 4 +- .../view/advanced_security_settings_menu.dart | 128 +++++----- .../cubit/credential_details_cubit.dart | 9 + .../home_credential_category_list.dart | 226 +++++++++++------- .../cubit/send_receive_home_cubit.dart | 57 +++++ .../tokens/send_to/view/send_to_page.dart | 8 +- .../tokens/token_page/cubit/tokens_cubit.dart | 123 +++++++++- lib/dashboard/profile/models/profile.dart | 25 +- .../profile/models/profile_setting.dart | 4 +- lib/enterprise/cubit/enterprise_cubit.dart | 15 +- pubspec.yaml | 2 +- 17 files changed, 594 insertions(+), 200 deletions(-) diff --git a/lib/app/shared/constants/parameters.dart b/lib/app/shared/constants/parameters.dart index 20a6eba73..79a5f7f93 100644 --- a/lib/app/shared/constants/parameters.dart +++ b/lib/app/shared/constants/parameters.dart @@ -115,3 +115,84 @@ class Parameters { // ThemeMode.light for talao static const ThemeMode defaultTheme = ThemeMode.dark; } + +final respond = [ + { + "token_instance": { + "is_unique": true, + "id": "431", + "holder_address_hash": "0x394c399dbA25B99Ab7708EdB505d755B3aa29997", + "image_url": "example.com/picture.png", + "animation_url": "example.com/video.mp4", + "external_app_url": "d-app.com", + "metadata": { + "year": 2023, + "tags": ["poap", "event"], + "name": "Social Listening Committee #2 Attendees", + "image_url": + "https://assets.poap.xyz/chanel-poap-4c-2023-logo-1675083420470.png", + "home_url": "https://app.poap.xyz/token/6292128", + "external_url": "https://api.poap.tech/metadata/99010/6292128", + "description": + "This is the POAP for attendees of the second Social Listening Committee.", + "attributes": [ + {"value": "01-Feb-2023", "trait_type": "startDate"}, + {"value": "01-Feb-2023", "trait_type": "endDate"}, + {"value": "false", "trait_type": "virtualEvent"}, + {"value": "Paris", "trait_type": "city"}, + {"value": "France", "trait_type": "country"}, + {"value": "https://www.chanel.com", "trait_type": "eventURL"} + ] + }, + "owner": { + "hash": "0xEb533ee5687044E622C69c58B1B12329F56eD9ad", + "implementation_name": "implementationName", + "name": "contractName", + "is_contract": true, + "private_tags": [ + { + "address_hash": "0xEb533ee5687044E622C69c58B1B12329F56eD9ad", + "display_name": "name to show", + "label": "label" + } + ], + "watchlist_names": [ + {"display_name": "name to show", "label": "label"} + ], + "public_tags": [ + { + "address_hash": "0xEb533ee5687044E622C69c58B1B12329F56eD9ad", + "display_name": "name to show", + "label": "label" + } + ], + "is_verified": true + }, + "token": { + "circulating_market_cap": "83606435600.3635", + "icon_url": + "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xdAC17F958D2ee523a2206206994597C13D831ec7/logo.png", + "name": "Tether USD", + "decimals": "6", + "symbol": "USDT", + "address": "0x394c399dbA25B99Ab7708EdB505d755B3aa29997", + "type": "ERC-20", + "holders": "837494234523", + "exchange_rate": "0.99", + "total_supply": "10000000" + } + }, + "value": "10000", + "token_id": "123", + "token": { + "name": "Tether USD", + "decimals": "16", + "symbol": "USDT", + "address": "0x394c399dbA25B99Ab7708EdB505d755B3aa29997", + "type": "ERC-20", + "holders": 837494234523, + "exchange_rate": "0.99", + "total_supply": "10000000" + } + } +]; diff --git a/lib/app/shared/extension/string_extension.dart b/lib/app/shared/extension/string_extension.dart index 422fed68e..6b01bc03a 100644 --- a/lib/app/shared/extension/string_extension.dart +++ b/lib/app/shared/extension/string_extension.dart @@ -1,4 +1,5 @@ import 'dart:convert'; +import 'dart:math'; import 'package:convert/convert.dart'; import 'package:decimal/decimal.dart'; @@ -46,7 +47,12 @@ extension StringExtension on String { } bool get isEVM { - if (this == 'ETH' || this == 'MATIC' || this == 'FTM' || this == 'BNB') { + if (this == 'ETH' || + this == 'MATIC' || + this == 'FTM' || + this == 'BNB' || + this == 'XTZ') { + // Etherlink - XTZ return true; } return false; @@ -66,4 +72,21 @@ extension StringExtension on String { Decimal.parse(number.toString()); return twoDecimalNumber.toDecimal().toString(); } + + BigInt get convertTo1e18 { + var value = BigInt.parse(this); + + const int targetLength = 18; + + final currentLength = value.toString().length; + + final zerosToAdd = targetLength - currentLength; + + if (zerosToAdd > 0) { + final factor = BigInt.from(pow(10, zerosToAdd)); + value = value * factor; + } + + return value; + } } diff --git a/lib/app/shared/helper_functions/helper_functions.dart b/lib/app/shared/helper_functions/helper_functions.dart index 95e612cd7..6faa1e8c3 100644 --- a/lib/app/shared/helper_functions/helper_functions.dart +++ b/lib/app/shared/helper_functions/helper_functions.dart @@ -2180,19 +2180,42 @@ Future?> checkVerifierAttestation({ return cnf['jwk'] as Map; } -String? getWalletAddress(CredentialSubjectModel credentialSubjectModel) { +/// walletaddress and blockchain type +(String?, BlockchainType?) getWalletAddress( + CredentialSubjectModel credentialSubjectModel, +) { if (credentialSubjectModel is TezosAssociatedAddressModel) { - return credentialSubjectModel.associatedAddress; + return ( + credentialSubjectModel.associatedAddress, + BlockchainType.tezos, + ); } else if (credentialSubjectModel is EthereumAssociatedAddressModel) { - return credentialSubjectModel.associatedAddress; + return ( + credentialSubjectModel.associatedAddress, + BlockchainType.ethereum, + ); } else if (credentialSubjectModel is PolygonAssociatedAddressModel) { - return credentialSubjectModel.associatedAddress; + return ( + credentialSubjectModel.associatedAddress, + BlockchainType.polygon, + ); } else if (credentialSubjectModel is BinanceAssociatedAddressModel) { - return credentialSubjectModel.associatedAddress; + return ( + credentialSubjectModel.associatedAddress, + BlockchainType.binance, + ); } else if (credentialSubjectModel is FantomAssociatedAddressModel) { - return credentialSubjectModel.associatedAddress; + return ( + credentialSubjectModel.associatedAddress, + BlockchainType.fantom, + ); + } else if (credentialSubjectModel is EtherlinkAssociatedAddressModel) { + return ( + credentialSubjectModel.associatedAddress, + BlockchainType.etherlink, + ); } - return null; + return (null, null); } Future fetchRpcUrl({ @@ -2202,7 +2225,8 @@ Future fetchRpcUrl({ String rpcUrl = ''; if (blockchainNetwork is BinanceNetwork || - blockchainNetwork is FantomNetwork) { + blockchainNetwork is FantomNetwork || + blockchainNetwork is EtherlinkNetwork) { rpcUrl = blockchainNetwork.rpcNodeUrl as String; } else { if (blockchainNetwork.networkname == 'Mainnet') { @@ -2225,3 +2249,21 @@ Future fetchRpcUrl({ return rpcUrl; } + +String getDidMethod(BlockchainType blockchainType) { + late String didMethod; + + switch (blockchainType) { + case BlockchainType.tezos: + didMethod = AltMeStrings.cryptoTezosDIDMethod; + + case BlockchainType.ethereum: + case BlockchainType.fantom: + case BlockchainType.polygon: + case BlockchainType.binance: + case BlockchainType.etherlink: + didMethod = AltMeStrings.cryptoEVMDIDMethod; + } + + return didMethod; +} diff --git a/lib/app/shared/models/blockchain_network/etherlink_network.dart b/lib/app/shared/models/blockchain_network/etherlink_network.dart index e03d07ea7..cd8302c70 100644 --- a/lib/app/shared/models/blockchain_network/etherlink_network.dart +++ b/lib/app/shared/models/blockchain_network/etherlink_network.dart @@ -25,10 +25,10 @@ class EtherlinkNetwork extends EthereumNetwork { factory EtherlinkNetwork.mainNet() => const EtherlinkNetwork( type: BlockchainType.etherlink, networkname: 'Mainnet', - apiUrl: Urls.moralisBaseUrl, + apiUrl: 'https://explorer.etherlink.com/api', chainId: 42793, chain: 'etherlink', - rpcNodeUrl: 'https://explorer.etherlink.com', + rpcNodeUrl: 'https://node.mainnet.etherlink.com', title: 'Etherlink Mainnet', subTitle: 'This network is the official Etherlink blockchain running Network.' @@ -38,10 +38,10 @@ class EtherlinkNetwork extends EthereumNetwork { factory EtherlinkNetwork.testNet() => const EtherlinkNetwork( type: BlockchainType.etherlink, networkname: 'Ghostnet', - apiUrl: Urls.moralisBaseUrl, + apiUrl: 'https://testnet.explorer.etherlink.com/api', chain: 'etherlink-testnet', chainId: 128123, - rpcNodeUrl: 'https://testnet.explorer.etherlink.com/', + rpcNodeUrl: 'https://node.ghostnet.etherlink.com', title: 'Etherlink Testnet (Ghostnet)', subTitle: 'This network is used to test protocol upgrades' ' (do not use it unless you are a developer).', diff --git a/lib/credentials/cubit/credentials_helper_function.dart b/lib/credentials/cubit/credentials_helper_function.dart index df3f2f487..6052ea084 100644 --- a/lib/credentials/cubit/credentials_helper_function.dart +++ b/lib/credentials/cubit/credentials_helper_function.dart @@ -16,19 +16,7 @@ Future generateAssociatedWalletCredential({ getLogger('CredentialsCubit - generateAssociatedWalletCredential'); log.i(blockchainType); try { - late String didMethod; - - switch (blockchainType) { - case BlockchainType.tezos: - didMethod = AltMeStrings.cryptoTezosDIDMethod; - - case BlockchainType.ethereum: - case BlockchainType.fantom: - case BlockchainType.polygon: - case BlockchainType.binance: - case BlockchainType.etherlink: - didMethod = AltMeStrings.cryptoEVMDIDMethod; - } + final didMethod = getDidMethod(blockchainType); log.i('didMethod - $didMethod'); @@ -38,6 +26,7 @@ Future generateAssociatedWalletCredential({ ); final String issuer = didKitProvider.keyToDID(didMethod, jwkKey); + log.i('jwkKey - $jwkKey'); log.i('didKitProvider.keyToDID - $issuer'); @@ -152,7 +141,7 @@ Future generateAssociatedWalletCredential({ id: id, issuer: issuer, issuanceDate: issuanceDate, - credentialSubjectModel: EthereumAssociatedAddressModel( + credentialSubjectModel: EtherlinkAssociatedAddressModel( id: did, accountName: cryptoAccountData.name, associatedAddress: cryptoAccountData.walletAddress, diff --git a/lib/dashboard/crypto_account_switcher/crypto_bottom_sheet/widgets/crypto_accont_item.dart b/lib/dashboard/crypto_account_switcher/crypto_bottom_sheet/widgets/crypto_accont_item.dart index 51ffb1809..3ea040e5d 100644 --- a/lib/dashboard/crypto_account_switcher/crypto_bottom_sheet/widgets/crypto_accont_item.dart +++ b/lib/dashboard/crypto_account_switcher/crypto_bottom_sheet/widgets/crypto_accont_item.dart @@ -86,9 +86,7 @@ class CryptoAccountItem extends StatelessWidget { InkWell( onTap: () async { await Clipboard.setData( - ClipboardData( - text: walletAddressExtracted, - ), + ClipboardData(text: cryptoAccountData.walletAddress), ); AlertMessage.showStateMessage( context: context, diff --git a/lib/dashboard/drawer/blockchain_settings/manage_accounts/widgets/manage_accounts_item.dart b/lib/dashboard/drawer/blockchain_settings/manage_accounts/widgets/manage_accounts_item.dart index 6bce8c935..e241ae134 100644 --- a/lib/dashboard/drawer/blockchain_settings/manage_accounts/widgets/manage_accounts_item.dart +++ b/lib/dashboard/drawer/blockchain_settings/manage_accounts/widgets/manage_accounts_item.dart @@ -85,9 +85,7 @@ class ManageAccountsItem extends StatelessWidget { InkWell( onTap: () async { await Clipboard.setData( - ClipboardData( - text: walletAddressExtracted, - ), + ClipboardData(text: cryptoAccountData.walletAddress), ); AlertMessage.showStateMessage( context: context, diff --git a/lib/dashboard/drawer/wallet_security/advanced_security_settings/view/advanced_security_settings_menu.dart b/lib/dashboard/drawer/wallet_security/advanced_security_settings/view/advanced_security_settings_menu.dart index abb229da8..4d196e8d3 100644 --- a/lib/dashboard/drawer/wallet_security/advanced_security_settings/view/advanced_security_settings_menu.dart +++ b/lib/dashboard/drawer/wallet_security/advanced_security_settings/view/advanced_security_settings_menu.dart @@ -46,79 +46,73 @@ class AdvancedSecuritySettingsView extends StatelessWidget { showPoweredBy: true, ), const SizedBox(height: Sizes.spaceSmall), - if (state.model.profileSetting.walletSecurityOptions - .verifySecurityIssuerWebsiteIdentity) - DrawerItem2( - title: l10n.verifyIssuerWebsiteIdentity, - subtitle: l10n.verifyIssuerWebsiteIdentitySubtitle, - trailing: SizedBox( - height: 25, - child: Switch( - onChanged: (value) async { - await context - .read() - .updateProfileSetting( - verifySecurityIssuerWebsiteIdentity: value, - ); - }, - value: state - .model - .profileSetting - .walletSecurityOptions - .verifySecurityIssuerWebsiteIdentity, - activeColor: Theme.of(context).colorScheme.primary, - ), + DrawerItem2( + title: l10n.verifyIssuerWebsiteIdentity, + subtitle: l10n.verifyIssuerWebsiteIdentitySubtitle, + trailing: SizedBox( + height: 25, + child: Switch( + onChanged: (value) async { + await context + .read() + .updateProfileSetting( + verifySecurityIssuerWebsiteIdentity: value, + ); + }, + value: state + .model + .profileSetting + .walletSecurityOptions + .verifySecurityIssuerWebsiteIdentity, + activeColor: Theme.of(context).colorScheme.primary, ), ), - if (state.model.profileSetting.walletSecurityOptions - .confirmSecurityVerifierAccess) - DrawerItem2( - title: l10n.confirmVerifierAccess, - subtitle: l10n.confirmVerifierAccessSubtitle, - trailing: SizedBox( - height: 25, - child: Switch( - onChanged: (value) async { - await context - .read() - .updateProfileSetting( - confirmSecurityVerifierAccess: value, - ); - }, - value: state - .model - .profileSetting - .walletSecurityOptions - .confirmSecurityVerifierAccess, - activeColor: Theme.of(context).colorScheme.primary, - ), + ), + DrawerItem2( + title: l10n.confirmVerifierAccess, + subtitle: l10n.confirmVerifierAccessSubtitle, + trailing: SizedBox( + height: 25, + child: Switch( + onChanged: (value) async { + await context + .read() + .updateProfileSetting( + confirmSecurityVerifierAccess: value, + ); + }, + value: state + .model + .profileSetting + .walletSecurityOptions + .confirmSecurityVerifierAccess, + activeColor: Theme.of(context).colorScheme.primary, ), ), - if (state.model.profileSetting.walletSecurityOptions - .secureSecurityAuthenticationWithPinCode) - DrawerItem2( - title: l10n.secureAuthenticationWithPINCode, - subtitle: l10n.secureAuthenticationWithPINCodeSubtitle, - trailing: SizedBox( - height: 25, - child: Switch( - onChanged: (value) async { - await context - .read() - .updateProfileSetting( - secureSecurityAuthenticationWithPinCode: - value, - ); - }, - value: state - .model - .profileSetting - .walletSecurityOptions - .secureSecurityAuthenticationWithPinCode, - activeColor: Theme.of(context).colorScheme.primary, - ), + ), + DrawerItem2( + title: l10n.secureAuthenticationWithPINCode, + subtitle: l10n.secureAuthenticationWithPINCodeSubtitle, + trailing: SizedBox( + height: 25, + child: Switch( + onChanged: (value) async { + await context + .read() + .updateProfileSetting( + secureSecurityAuthenticationWithPinCode: + value, + ); + }, + value: state + .model + .profileSetting + .walletSecurityOptions + .secureSecurityAuthenticationWithPinCode, + activeColor: Theme.of(context).colorScheme.primary, ), ), + ), ], ), ), diff --git a/lib/dashboard/home/tab_bar/credentials/detail/cubit/credential_details_cubit.dart b/lib/dashboard/home/tab_bar/credentials/detail/cubit/credential_details_cubit.dart index 410e3cc65..bc44f2e0e 100644 --- a/lib/dashboard/home/tab_bar/credentials/detail/cubit/credential_details_cubit.dart +++ b/lib/dashboard/home/tab_bar/credentials/detail/cubit/credential_details_cubit.dart @@ -5,6 +5,7 @@ import 'package:altme/oidc4vc/verify_encoded_data.dart'; import 'package:altme/polygon_id/polygon_id.dart'; import 'package:altme/selective_disclosure/selective_disclosure.dart'; import 'package:did_kit/did_kit.dart'; +import 'package:dio/dio.dart'; import 'package:equatable/equatable.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:json_annotation/json_annotation.dart'; @@ -153,6 +154,10 @@ class CredentialDetailsCubit extends Cubit { statusListUri, headers: headers, isCachingEnabled: customOidc4vcProfile.statusListCache, + options: Options().copyWith( + sendTimeout: const Duration(seconds: 10), + receiveTimeout: const Duration(seconds: 10), + ), ); final payload = jwtDecode.parseJwt(response.toString()); @@ -234,6 +239,10 @@ class CredentialDetailsCubit extends Cubit { statusListUri, headers: headers, isCachingEnabled: customOidc4vcProfile.statusListCache, + options: Options().copyWith( + sendTimeout: const Duration(seconds: 10), + receiveTimeout: const Duration(seconds: 10), + ), ); final payload = jwtDecode.parseJwt(response.toString()); 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 a40ed1508..238402b41 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 @@ -1,11 +1,13 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/wallet/wallet.dart'; +import 'package:did_kit/did_kit.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:key_generator/key_generator.dart'; import 'package:oidc4vc/oidc4vc.dart'; -class HomeCredentialCategoryList extends StatelessWidget { +class HomeCredentialCategoryList extends StatefulWidget { const HomeCredentialCategoryList({ super.key, required this.credentials, @@ -15,6 +17,28 @@ class HomeCredentialCategoryList extends StatelessWidget { final List credentials; final RefreshCallback onRefresh; + @override + State createState() => + _HomeCredentialCategoryListState(); +} + +class _HomeCredentialCategoryListState + extends State { + Future getDid() async { + final currentAccount = context.read().state.currentAccount!; + + final didMethod = getDidMethod(currentAccount.blockchainType); + + final String jwkKey = await KeyGenerator().jwkFromSecretKey( + secretKey: currentAccount.secretKey, + accountType: currentAccount.blockchainType.accountType, + ); + + final String issuer = DIDKitProvider().keyToDID(didMethod, jwkKey); + + return issuer; + } + @override Widget build(BuildContext context) { return BlocBuilder( @@ -25,90 +49,130 @@ class HomeCredentialCategoryList extends StatelessWidget { .profileSetting.selfSovereignIdentityOptions.customOidc4vcProfile; return RefreshIndicator( - onRefresh: onRefresh, + onRefresh: widget.onRefresh, child: Padding( padding: const EdgeInsets.symmetric(horizontal: Sizes.space2XSmall), - child: ListView( - scrollDirection: Axis.vertical, - children: getCredentialCategorySorted.where( - (category) { - return advanceSettingsState.categoryIsEnabledMap[category] ?? - true; - }, - ).map((category) { - final categorizedCredentials = credentials.where( - (element) { - /// id credential category does not match, do not show - if (element.credentialPreview.credentialSubjectModel - .credentialCategory != - category) { - return false; - } - - /// wallet credential to be shown always - if (element.credentialPreview.credentialSubjectModel - .credentialSubjectType == - CredentialSubjectType.walletCredential) { - if (!profileModel.isDeveloperMode) { - return false; - } - } - - /// crypto credential account to be shown always - if (element.credentialPreview.credentialSubjectModel - .credentialSubjectType.isBlockchainAccount) { - /// only show crypto card with matches current account - /// wallet address - final String? currentWalletAddress = context - .read() - .state - .currentAccount - ?.walletAddress; - - final String? walletAddress = getWalletAddress( - element.credentialPreview.credentialSubjectModel, - ); - - if (currentWalletAddress.toString() != - walletAddress.toString()) { - return false; - } - } - - if (customOidc4vcProfile.vcFormatType.vcValue == - VCFormatType.auto.vcValue) { - return true; - } - - /// do not load the credential if vc format is different - if (element.credentialPreview.credentialSubjectModel - .credentialSubjectType != - CredentialSubjectType.walletCredential) { - if (customOidc4vcProfile.vcFormatType.vcValue != - element.getFormat) { - return false; - } - } - - return true; - }, - ).toList(); - if (categorizedCredentials.isEmpty) { - if (category.showInHomeIfListEmpty) { - return HomeCredentialCategoryItem( - credentials: const [], - credentialCategory: category, + child: FutureBuilder( + future: getDid(), + builder: (context, snapshot) { + switch (snapshot.connectionState) { + case ConnectionState.done: + final issuerDid = snapshot.data!; + return ListView( + scrollDirection: Axis.vertical, + children: getCredentialCategorySorted.where( + (category) { + return advanceSettingsState + .categoryIsEnabledMap[category] ?? + true; + }, + ).map((category) { + final categorizedCredentials = widget.credentials.where( + (element) { + /// id credential category does not match, do + /// not show + if (element.credentialPreview.credentialSubjectModel + .credentialCategory != + category) { + return false; + } + + /// wallet credential to be shown always + if (element.credentialPreview.credentialSubjectModel + .credentialSubjectType == + CredentialSubjectType.walletCredential) { + if (!profileModel.isDeveloperMode) { + return false; + } + } + + /// crypto credential account to be shown always + if (element.credentialPreview.credentialSubjectModel + .credentialSubjectType.isBlockchainAccount) { + /// only show crypto card with matches current + /// account wallet address + + final currentAccount = context + .read() + .state + .currentAccount; + + if (currentAccount != null) { + final currentWalletAddress = + currentAccount.walletAddress; + + final currentBlockchainType = + currentAccount.blockchainType; + + final (walletAddress, blockchainType) = + getWalletAddress( + element + .credentialPreview.credentialSubjectModel, + ); + + final matchesWalletAddress = + currentWalletAddress != + walletAddress.toString(); + + final matchesBlockchainType = + currentBlockchainType != blockchainType; + + if (matchesWalletAddress || + matchesBlockchainType) { + return false; + } + } + } + + /// if crypto did matches with vc. + if (issuerDid == + element.credentialPreview.credentialSubjectModel + .id) { + return true; + } + + if (customOidc4vcProfile.vcFormatType.vcValue == + VCFormatType.auto.vcValue) { + return true; + } + + if (element.credentialPreview.credentialSubjectModel + .credentialSubjectType != + CredentialSubjectType.walletCredential) { + /// do not load the credential if vc format is + /// different + if (customOidc4vcProfile.vcFormatType.vcValue != + element.getFormat) { + return false; + } + } + + return true; + }, + ).toList(); + if (categorizedCredentials.isEmpty) { + if (category.showInHomeIfListEmpty) { + return HomeCredentialCategoryItem( + credentials: const [], + credentialCategory: category, + ); + } else { + return const SizedBox.shrink(); + } + } else { + return HomeCredentialCategoryItem( + credentials: categorizedCredentials, + credentialCategory: category, + ); + } + }).toList(), ); - } else { - return const SizedBox.shrink(); - } - } else { - return HomeCredentialCategoryItem( - credentials: categorizedCredentials, - credentialCategory: category, - ); + case ConnectionState.waiting: + case ConnectionState.none: + case ConnectionState.active: + return const SizedBox(); } - }).toList(), + }, ), ), ); diff --git a/lib/dashboard/home/tab_bar/tokens/send_receive_home/cubit/send_receive_home_cubit.dart b/lib/dashboard/home/tab_bar/tokens/send_receive_home/cubit/send_receive_home_cubit.dart index 28de298e1..9fb5d59a5 100644 --- a/lib/dashboard/home/tab_bar/tokens/send_receive_home/cubit/send_receive_home_cubit.dart +++ b/lib/dashboard/home/tab_bar/tokens/send_receive_home/cubit/send_receive_home_cubit.dart @@ -84,6 +84,12 @@ class SendReceiveHomeCubit extends Cubit { walletAddress: walletAddress, contractAddress: state.selectedToken.contractAddress, ); + } else if (blockchainType == BlockchainType.etherlink) { + operations = await _getEtherlinkOperations( + walletAddress: walletAddress, + contractAddress: state.selectedToken.contractAddress, + ehtereumNetwork: manageNetworkCubit.state.network as EthereumNetwork, + ); } else { operations = await _getEthereumOperations( walletAddress: walletAddress, @@ -186,6 +192,57 @@ class SendReceiveHomeCubit extends Cubit { } } + Future> _getEtherlinkOperations({ + required String walletAddress, + required String contractAddress, + required EthereumNetwork ehtereumNetwork, + }) async { + List result; + List operations = []; + + try { + final dynamic response = await client.get( + '${ehtereumNetwork.apiUrl}/v2/addresses/$walletAddress/transactions', + headers: {'Content-Type': 'application/json'}, + ); + + result = response['items'] as List; + + operations = result.map( + (dynamic e) { + return OperationModel( + type: '', + id: -1, + level: 0, + timestamp: e['timestamp'] as String, + block: e['block'].toString(), + hash: e['hash'] as String, + counter: 0, + sender: OperationAddressModel(address: e['from']['hash'] as String), + gasLimit: int.parse(e['gas_limit'].toString()), + gasUsed: int.parse(e['gas_used'].toString()), + storageLimit: 0, + storageUsed: 0, + bakerFee: 0, + storageFee: 0, + allocationFee: 0, + target: OperationAddressModel(address: e['to']['hash'] as String), + amount: e['value'].toString(), + status: e['result'].toString(), + hasInternals: false, + ); + }, + ).toList(); + } catch (e, s) { + getLogger(toString()).e('having issue: $e, stack: $s'); + } + + operations.sort( + (a, b) => b.dateTime.compareTo(a.dateTime), + ); + return operations; + } + Future> _getEthereumOperations({ required String walletAddress, required String contractAddress, diff --git a/lib/dashboard/home/tab_bar/tokens/send_to/view/send_to_page.dart b/lib/dashboard/home/tab_bar/tokens/send_to/view/send_to_page.dart index 26f101495..a7560875c 100644 --- a/lib/dashboard/home/tab_bar/tokens/send_to/view/send_to_page.dart +++ b/lib/dashboard/home/tab_bar/tokens/send_to/view/send_to_page.dart @@ -83,13 +83,9 @@ class _SendToViewState extends State textAlign: TextAlign.center, style: Theme.of(context).textTheme.titleLarge, ), - const SizedBox( - height: Sizes.spaceXLarge, - ), + const SizedBox(height: Sizes.spaceXLarge), const FromAccountWidget(), - const SizedBox( - height: Sizes.spaceNormal, - ), + const SizedBox(height: Sizes.spaceNormal), WithdrawalAddressInputView( withdrawalAddressController: withdrawalAddressController, caption: l10n.to, diff --git a/lib/dashboard/home/tab_bar/tokens/token_page/cubit/tokens_cubit.dart b/lib/dashboard/home/tab_bar/tokens/token_page/cubit/tokens_cubit.dart index c635fc1ce..d71e0e5fe 100644 --- a/lib/dashboard/home/tab_bar/tokens/token_page/cubit/tokens_cubit.dart +++ b/lib/dashboard/home/tab_bar/tokens/token_page/cubit/tokens_cubit.dart @@ -81,6 +81,7 @@ class TokensCubit extends Cubit { if (state.blockchainType != selectedAccountBlockchainType) { emit(state.reset(blockchainType: selectedAccountBlockchainType)); } + if (state.offset == 0) { emit(state.fetching()); } @@ -92,6 +93,13 @@ class TokensCubit extends Cubit { limit: _limit, offset: state.offset, ); + } else if (selectedAccountBlockchainType == BlockchainType.etherlink) { + await getTokensOnEtherlink( + walletAddress: walletAddress, + ethereumNetwork: networkCubit.state.network as EthereumNetwork, + limit: _limit, + offset: state.offset, + ); } else { await getTokensOnEthereum( walletAddress: walletAddress, @@ -116,6 +124,87 @@ class TokensCubit extends Cubit { } } + Future getTokensOnEtherlink({ + required String walletAddress, + required EthereumNetwork ethereumNetwork, + required int limit, + required int offset, + }) async { + if (offset > 0) { + //because at this time pagination not supported for Ethereum tokens + return; + } + final baseUrl = ethereumNetwork.apiUrl; + + final List tokensBalancesJsonArray = await client.get( + '$baseUrl/v2/addresses/$walletAddress/token-balances', + headers: {'Content-Type': 'application/json'}, + ) as List; + + List newData = []; + + if (tokensBalancesJsonArray.isNotEmpty) { + newData = tokensBalancesJsonArray.map( + (dynamic json) { + final icon = (json['token_instance'] + as Map)['metadata']['image_url']; + return TokenModel( + contractAddress: (json['token_instance'] + as Map)['owner']['hash'] as String, + name: + ((json['token'] as Map)['name'] as String?) ?? + '', + symbol: ((json['token'] as Map)['symbol'] + as String?) ?? + '', + balance: (json['value'] as String?) ?? '', + decimals: ((json['token_instance'] as Map)['token'] + ['decimals'] ?? + 0) + .toString(), + thumbnailUri: '', + standard: 'erc20', + icon: icon.toString(), + decimalsToShow: 2, + ); + }, + ).toList(); + } + + if (offset == 0) { + final ethereumBaseToken = await _getBaseTokenBalanceOnEtherlink( + walletAddress, + ethereumNetwork.chain, + ethereumNetwork, + ); + + if (ethereumBaseToken != null) { + newData.insert(0, ethereumBaseToken); + } + data = newData; + } else { + data.addAll(newData); + } + + data = await setUSDValues(data); + double totalBalanceInUSD = 0; + for (final tokenElement in data) { + totalBalanceInUSD += tokenElement.balanceInUSD; + } + data.sort((a, b) => b.balanceInUSD.compareTo(a.balanceInUSD)); + + emit( + state.copyWith( + status: AppStatus.success, + data: data.toSet(), + totalBalanceInUSD: totalBalanceInUSD, + ), + ); + await checkIfItNeedsToVerifyMnemonic( + totalBalanceInUSD: totalBalanceInUSD, + ); + } + Future getTokensOnEthereum({ required String walletAddress, required EthereumNetwork ethereumNetwork, @@ -161,7 +250,7 @@ class TokensCubit extends Cubit { } if (offset == 0) { - final ethereumBaseToken = await _getBaseTokenBalanceOnEth( + final ethereumBaseToken = await _getBaseTokenBalanceOnEthereum( walletAddress, ethereumNetwork.chain, ethereumNetwork, @@ -421,7 +510,37 @@ class TokensCubit extends Cubit { } } - Future _getBaseTokenBalanceOnEth( + Future _getBaseTokenBalanceOnEtherlink( + String walletAddress, + String chain, + EthereumNetwork ethereumNetwork, + ) async { + try { + final response = await client.get( + '${ethereumNetwork.apiUrl}/v2/addresses/$walletAddress', + headers: {'Content-Type': 'application/json'}, + ) as Map; + + final coinBalance = response['coin_balance'].toString().convertTo1e18; + + return TokenModel( + contractAddress: '', + name: 'Etherlink', + symbol: 'XTZ', + icon: ethereumNetwork.mainTokenIcon, + balance: coinBalance.toString(), + decimals: ethereumNetwork.mainTokenDecimal, + standard: 'ERC20', + decimalsToShow: 5, + ); + ; + } catch (e, s) { + getLogger(toString()).e('error: $e, stack: $s'); + return null; + } + } + + Future _getBaseTokenBalanceOnEthereum( String walletAddress, String chain, EthereumNetwork ethereumNetwork, diff --git a/lib/dashboard/profile/models/profile.dart b/lib/dashboard/profile/models/profile.dart index 51de1dc8c..deeb2ccff 100644 --- a/lib/dashboard/profile/models/profile.dart +++ b/lib/dashboard/profile/models/profile.dart @@ -95,7 +95,12 @@ class ProfileModel extends Equatable { ), settingsMenu: SettingsMenu.initial(), version: '', - walletSecurityOptions: WalletSecurityOptions.initial(), + walletSecurityOptions: const WalletSecurityOptions( + confirmSecurityVerifierAccess: true, + displaySecurityAdvancedSettings: true, + secureSecurityAuthenticationWithPinCode: true, + verifySecurityIssuerWebsiteIdentity: true, + ), ), ); @@ -142,7 +147,12 @@ class ProfileModel extends Equatable { ), settingsMenu: SettingsMenu.initial(), version: '', - walletSecurityOptions: WalletSecurityOptions.initial(), + walletSecurityOptions: const WalletSecurityOptions( + confirmSecurityVerifierAccess: true, + displaySecurityAdvancedSettings: true, + secureSecurityAuthenticationWithPinCode: true, + verifySecurityIssuerWebsiteIdentity: true, + ), ), ); @@ -211,7 +221,12 @@ class ProfileModel extends Equatable { ), settingsMenu: SettingsMenu.initial(), version: '', - walletSecurityOptions: WalletSecurityOptions.initial(), + walletSecurityOptions: const WalletSecurityOptions( + confirmSecurityVerifierAccess: true, + displaySecurityAdvancedSettings: true, + secureSecurityAuthenticationWithPinCode: true, + verifySecurityIssuerWebsiteIdentity: true, + ), ), ); @@ -283,8 +298,8 @@ class ProfileModel extends Equatable { walletSecurityOptions: const WalletSecurityOptions( confirmSecurityVerifierAccess: true, displaySecurityAdvancedSettings: true, - secureSecurityAuthenticationWithPinCode: false, - verifySecurityIssuerWebsiteIdentity: false, + secureSecurityAuthenticationWithPinCode: true, + verifySecurityIssuerWebsiteIdentity: true, ), ), ); diff --git a/lib/dashboard/profile/models/profile_setting.dart b/lib/dashboard/profile/models/profile_setting.dart index da880f468..9f25be133 100644 --- a/lib/dashboard/profile/models/profile_setting.dart +++ b/lib/dashboard/profile/models/profile_setting.dart @@ -90,11 +90,11 @@ class BlockchainOptions extends Equatable { required this.hederaSupport, required this.infuraRpcNode, required this.polygonSupport, - required this.etherlinkSupport, required this.tezosSupport, required this.tzproRpcNode, this.tzproApiKey, this.infuraApiKey, + this.etherlinkSupport, }); factory BlockchainOptions.fromJson(Map json) => @@ -119,7 +119,7 @@ class BlockchainOptions extends Equatable { final String? infuraApiKey; final bool infuraRpcNode; final bool polygonSupport; - final bool etherlinkSupport; + final bool? etherlinkSupport; final bool tezosSupport; final String? tzproApiKey; final bool tzproRpcNode; diff --git a/lib/enterprise/cubit/enterprise_cubit.dart b/lib/enterprise/cubit/enterprise_cubit.dart index 674d1707b..afecc9843 100644 --- a/lib/enterprise/cubit/enterprise_cubit.dart +++ b/lib/enterprise/cubit/enterprise_cubit.dart @@ -4,6 +4,7 @@ import 'package:altme/app/app.dart'; import 'package:altme/credentials/credentials.dart'; import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/oidc4vc/oidc4vc.dart'; +import 'package:dio/dio.dart'; import 'package:equatable/equatable.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:json_annotation/json_annotation.dart'; @@ -138,7 +139,7 @@ class EnterpriseCubit extends Cubit { }; final response = await client.post( - '$url/configuration', + '${url}configuration', headers: headers, data: data, ); @@ -168,7 +169,7 @@ class EnterpriseCubit extends Cubit { } Future getNonce(String url) async { - final dynamic getRepsponse = await client.get('$url/nonce'); + final dynamic getRepsponse = await client.get('${url}nonce'); final nonce = getRepsponse['nonce'].toString(); return nonce; } @@ -237,7 +238,7 @@ class EnterpriseCubit extends Cubit { /// get vc final response = await client.post( - '$url/token', + '${url}token', headers: { 'Content-Type': 'application/x-www-form-urlencoded', }, @@ -282,6 +283,10 @@ class EnterpriseCubit extends Cubit { final response = await client.get( uri, headers: headers, + options: Options().copyWith( + sendTimeout: const Duration(seconds: 10), + receiveTimeout: const Duration(seconds: 10), + ), ); final payload = profileCubit.jwtDecode.parseJwt(response.toString()); @@ -362,6 +367,10 @@ class EnterpriseCubit extends Cubit { final response = await client.get( uri, headers: headers, + options: Options().copyWith( + sendTimeout: const Duration(seconds: 10), + receiveTimeout: const Duration(seconds: 10), + ), ); final payload = diff --git a/pubspec.yaml b/pubspec.yaml index e743a7ba2..58a386022 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: altme description: AltMe Flutter App -version: 2.10.3+485 +version: 2.10.8+490 publish_to: "none" # Remove this line if you wish to publish to pub.dev environment: sdk: ">=3.1.0 <4.0.0"