Skip to content
This repository was archived by the owner on Sep 17, 2024. It is now read-only.

Pre main qa #99

Merged
merged 31 commits into from
Oct 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
cbe62f8
feat: Added findAccountsLastIndex to account use case
reasje Oct 16, 2023
afb6572
feat: Added private key validation
reasje Oct 16, 2023
0279c24
impv: Add new account settings page
reasje Oct 16, 2023
ecdbedd
feat: Added imported chip to account item & limit accounts list size
reasje Oct 16, 2023
8901eb1
feat: Added add accounts dialog
reasje Oct 16, 2023
a476ad2
feat: Added import account page
reasje Oct 16, 2023
d507379
feat: Added remove account functionality
reasje Oct 16, 2023
32fe801
fix: fee exceed in send crypto & tx recording number conversion
reasje Oct 16, 2023
63ebe97
fix: Removed add accounts for better UX
reasje Oct 17, 2023
a189f4f
feat: Added private key validation & moved pub key validation to vali…
reasje Oct 17, 2023
b7049ad
feat: Updated single line info for custom icons & functions
reasje Oct 17, 2023
7ed6fc4
feat: Added passcode athentication page for verifying user
reasje Oct 17, 2023
157638d
feat: Added second hint for passcode pages
reasje Oct 17, 2023
b236cf8
feat: Added remove account functionality
reasje Oct 17, 2023
c16446a
feat: Added view private key button to qr code page
reasje Oct 17, 2023
3af7afd
feat: Added show private key dialog
reasje Oct 17, 2023
91bdf2b
feat: Updated show accounts dialog & added import button
reasje Oct 17, 2023
2b575b0
Merge pull request #95 from MXCzkEVM/account_management_feat
reasje Oct 17, 2023
197143f
fix: UI fixes on qr code view private key button
reasje Oct 17, 2023
409cc14
Merge pull request #96 from MXCzkEVM/account_management_feat
reasje Oct 17, 2023
655b25f
fix: Imported accounts numbering & separation
reasje Oct 18, 2023
187f634
fix: Added accounts numbering & separation
reasje Oct 18, 2023
49327f7
fix: Added duplicate account check in import
reasje Oct 18, 2023
120dc34
Merge pull request #97 from MXCzkEVM/account_management_feat
reasje Oct 18, 2023
2cf1d2e
feat: Added utils with email app launch & check
reasje Oct 19, 2023
bafa7ec
feat: Added email app url
reasje Oct 19, 2023
16bb77b
fix: Disable email button if there is no email app
reasje Oct 19, 2023
6a4a74d
fix: Launch email app if not logged in IOS
reasje Oct 19, 2023
0f7420c
Merge pull request #98 from MXCzkEVM/email_launch_fix
reasje Oct 19, 2023
a60f3cf
feat: Updated app version to 1.5.2
reasje Oct 19, 2023
69f29ae
Merge branch 'main' into pre_main_qa
reasje Oct 19, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions assets/flutter_i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -280,13 +280,25 @@
"checking_balance": "Checking balance...",
"no_balance": "You have no balance",
"no_balance_tip": "You currently have no MXC in your wallet.\nYou can either receive tokens or choose to claim a username later.",
"signature_request": "Signature request",
"signature_request": "Signature request",
"sign": "Sign",
"insufficient_balance": "Insufficient balance",
"unregistered_mns_notice": "Oops! Sending tokens to a username as elusive as a unicorn in a forest. Check if they're registered!",
"insufficient_balance_for_fee": "Insufficient balance for fee",
"send_&_receive": "Send & Receive",
"contract": "Contract",
"decimals": "Decimals",
"add_token_success_message" : "Hooray! The token has been successfully added to your AXS wallet! 🎉"
"add_token_success_message": "Hooray! The token has been successfully added to your AXS wallet! 🎉",
"import_account": "Import account",
"add_account": "Add account",
"private_key": "Private key",
"import_notice": "Imported accounts won’t be associated with your AXS wallet Secret Recovery Phrase.",
"imported": "Imported",
"view_private_key": "View private key",
"view_private_key_notice": "Warning: Never disclose this key. Anyone with your private keys can steal any assets held in your account.",
"removing_account": "Removing account",
"removing_account_warning": "Are you sure you want to remove this account?",
"remove": "Remove",
"duplicate_account_import_notice": "The account you are trying to import is a duplicate",
"unable_to_launch_email_app": "Unable to launch email app"
}
32 changes: 21 additions & 11 deletions lib/common/components/list/single_line_info_item.dart
Original file line number Diff line number Diff line change
@@ -1,23 +1,29 @@
import 'package:datadashwallet/features/dapps/subfeatures/open_dapp/open_dapp_presenter.dart';
import 'package:datadashwallet/common/common.dart';
import 'package:datadashwallet/core/src/providers/providers_use_cases.dart';
import 'package:flutter/material.dart';
import 'package:flutter_i18n/flutter_i18n.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:mxc_ui/mxc_ui.dart';

class SingleLineInfoItem extends HookConsumerWidget {
const SingleLineInfoItem({
super.key,
required this.title,
required this.value,
this.hint,
});
const SingleLineInfoItem(
{super.key,
required this.title,
required this.value,
this.hint,
this.valueActionIcon});

final String title;
final String value;
final String? hint;
final Widget? valueActionIcon;

@override
Widget build(BuildContext context, WidgetRef ref) {
final presenter = ref.read(openDAppPageContainer.actions);
final isAddress = presenter.isAddress(value);
late final _chainConfigurationUseCase =
ref.read(chainConfigurationUseCaseProvider);

final isAddress = Validation.isAddress(value);
return Padding(
padding: const EdgeInsets.symmetric(vertical: Sizes.spaceXSmall),
child: Row(
Expand All @@ -34,7 +40,9 @@ class SingleLineInfoItem extends HookConsumerWidget {
),
Expanded(
child: InkWell(
onTap: isAddress ? () => presenter.launchAddress(value) : null,
onTap: isAddress
? () => _chainConfigurationUseCase.launchAddress(value)
: null,
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Expand All @@ -46,7 +54,9 @@ class SingleLineInfoItem extends HookConsumerWidget {
textAlign: TextAlign.end,
),
),
if (isAddress) ...[
if (valueActionIcon != null)
valueActionIcon!
else if (isAddress) ...[
const SizedBox(width: 8),
Icon(
MxcIcons.external_link,
Expand Down
2 changes: 2 additions & 0 deletions lib/common/urls.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,6 @@ class Urls {

static const String iOSUrl =
'https://apps.apple.com/us/app/axs-decentralized-wallet/id6460891587';

static const String emailApp = 'mailto:';
}
2 changes: 1 addition & 1 deletion lib/common/utils/formatter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ class Formatter {

static String convertWeiToEth(String inputString, int tokenDecimal) {
// 10^18 = 1000000000000000000 but we want to have up to 2 digits accuracy
if (double.parse(inputString).toDouble() < 10000000000000000) {
if (double.parse(inputString).toDouble() < 1000000000000000) {
return '0';
}
final valueDouble = double.parse(inputString).toDouble() / pow(10, 18);
Expand Down
21 changes: 21 additions & 0 deletions lib/common/utils/utils.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,24 @@
import 'package:datadashwallet/common/common.dart';
import 'package:url_launcher/url_launcher.dart';

export 'formatter.dart';
export 'permission.dart';
export 'validation.dart';

class Utils {
static Future<bool> isEmailAppAvailable() async {
final url = Uri.parse(Urls.emailApp);

return await canLaunchUrl(url);
}

static Future<void> launchEmailApp() async {
final url = Uri.parse(Urls.emailApp);

if (await canLaunchUrl(url)) {
await launchUrl(url);
} else {
throw 'unable_to_launch_email_app';
}
}
}
32 changes: 32 additions & 0 deletions lib/common/utils/validation.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import 'package:datadashwallet/common/config.dart';
import 'package:flutter/material.dart';
import 'package:flutter_i18n/flutter_i18n.dart';
import 'package:web3dart/web3dart.dart';


class Validation {
static String? notEmpty(BuildContext context, String? value,
Expand Down Expand Up @@ -53,6 +55,17 @@ class Validation {
return null;
}

static String? checkEthereumPrivateKey(BuildContext context, String value) {
String ethereumPrivateKeyPattern = r'^[0-9a-fA-F]{64}$';

if (!RegExp(ethereumPrivateKeyPattern, caseSensitive: false)
.hasMatch(value)) {
return FlutterI18n.translate(context, 'invalid_format');
}

return null;
}

static String? checkMnsValidation(BuildContext context, String value) {
if (!((value.endsWith('.mxc') || value.endsWith('.MXC')) &&
value.length > 4)) {
Expand Down Expand Up @@ -118,4 +131,23 @@ class Validation {

return regex.hasMatch(value);
}


static bool isAddress(String address) {
try {
EthereumAddress.fromHex(address);
return true;
} catch (e) {
return false;
}
}

static bool isPrivateKey(String privateKey) {
try {
EthPrivateKey.fromHex(privateKey);
return true;
} catch (e) {
return false;
}
}
}
15 changes: 13 additions & 2 deletions lib/features/common/account/account_cache_repository.dart
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,19 @@ class AccountCacheRepository extends GlobalCacheRepository {
List<Account> get accountItems => accounts.value;
Account get accountItem => account.value!;

void addAccount(Account item) => accounts.value = [...accounts.value, item];
void addAccount(Account item, {int? index}) {
if (index == null) {
accounts.value = [...accounts.value, item];
} else {
final newList = accounts.value;
newList.insert(index, item);
accounts.value = newList;
}
}

void removeAccount(Account item) => accounts.value =
accounts.value.where((e) => e.name != item.name).toList();
accounts.value.where((e) => e.address != item.address).toList();

void updateAccount(Account item) => accounts.value = accounts.value.map((e) {
if (item.address == account.value!.address) {
account.value = item;
Expand All @@ -67,6 +77,7 @@ class AccountCacheRepository extends GlobalCacheRepository {
}
return e;
}).toList();

void resetAccounts() => accounts.value = [];

void setXsdConversionRate(double value) => xsdConversionRate.value = value;
Expand Down
27 changes: 25 additions & 2 deletions lib/features/common/account/account_use_case.dart
Original file line number Diff line number Diff line change
Expand Up @@ -32,18 +32,41 @@ class AccountUseCase extends ReactiveUseCase {
update(account, item);
}

void addAccount(Account item) async {
_accountCacheRepository.addAccount(item);
void addAccount(Account item, {int? index}) async {
_accountCacheRepository.addAccount(item, index: index);
final items = _accountCacheRepository.accountItems;
update(account, item);
update(accounts, items);
getAccountsNames();
}

/// Deletes the given account, If the account is selected will select the index 0 account
/// This is only used to delete the imported accounts.
void removeAccount(Account item) async {
_accountCacheRepository.removeAccount(item);
final items = _accountCacheRepository.accountItems;
update(accounts, items);
}

bool isAccountSelected(Account item) {
return (item.address == account.value!.address);
}

void changeAccount(Account item) {
update(account, item);
}

int findAccountsLastIndex() {
int lastIndex = 0;
for (Account account in accounts.value.reversed) {
if (!account.isCustom) {
lastIndex = int.parse(account.name);
break;
}
}
return lastIndex;
}

void resetXsdConversionRate(double value) {
_accountCacheRepository.setXsdConversionRate(value);
update(xsdConversionRate, value);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -350,12 +350,7 @@ class OpenDAppPresenter extends CompletePresenter<OpenDAppState> {
}

bool isAddress(String address) {
try {
EthereumAddress.fromHex(address);
return true;
} catch (e) {
return false;
}
return Validation.isAddress(address);
}

void addAsset(int id, Map<String, dynamic> data,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,9 @@ class AddAssetInfo extends ConsumerWidget {
title: FlutterI18n.translate(context, 'contract'),
value: contractAddress ?? ''));
infoList.add(SingleLineInfoItem(
title: FlutterI18n.translate(context, 'symbol'), value: symbol ?? ''));
title: FlutterI18n.translate(context, 'symbol'),
value: symbol ?? '',
));
infoList.add(SingleLineInfoItem(
title: FlutterI18n.translate(context, 'decimals'),
value: decimalsString));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,23 +150,25 @@ class SendCryptoPresenter extends CompletePresenter<SendCryptoState> {

double sumBalance = token.balance! - double.parse(amount);
estimatedGasFee = await _estimatedFee(recipientAddress);
sumBalance -= estimatedGasFee?.gasFee ?? 0.0;
final estimatedFee = estimatedGasFee == null
? '--'
: Validation.isExpoNumber(estimatedGasFee.gasFee.toString())
? '0.000'
: estimatedGasFee.gasFee.toString();

final result = await showTransactionDialog(context!,
amount: amount,
balance: sumBalance.toString(),
token: token,
network: state.network?.label ?? '--',
from: state.account!.address,
to: recipient,
estimatedFee: estimatedFee,
onTap: (transactionType) => _nextTransactionStep(transactionType),
networkSymbol: state.network?.symbol ?? '--');
if (estimatedGasFee != null) {
sumBalance -= estimatedGasFee.gasFee;
final estimatedFee = estimatedGasFee == null
? '--'
: Validation.isExpoNumber(estimatedGasFee.gasFee.toString())
? '0.000'
: estimatedGasFee.gasFee.toString();

final result = await showTransactionDialog(context!,
amount: amount,
balance: sumBalance.toString(),
token: token,
network: state.network?.label ?? '--',
from: state.account!.address,
to: recipient,
estimatedFee: estimatedFee,
onTap: (transactionType) => _nextTransactionStep(transactionType),
networkSymbol: state.network?.symbol ?? '--');
}
}

String? checkAmountCeiling() {
Expand Down Expand Up @@ -203,7 +205,11 @@ class SendCryptoPresenter extends CompletePresenter<SendCryptoState> {

return gasFee;
} catch (e, s) {
addError(e, s);
if (e is RPCError) {
String errorMessage = e.message;
errorMessage = changeErrorMessage(errorMessage);
addError(errorMessage);
}
} finally {
loading = false;
}
Expand All @@ -229,7 +235,7 @@ class SendCryptoPresenter extends CompletePresenter<SendCryptoState> {
hash: res,
status: TransactionStatus.pending,
type: TransactionType.sent,
value: amount.getValueInUnit(EtherUnit.wei).toString(),
value: amount.getValueInUnitBI(EtherUnit.wei).toString(),
token: token,
timeStamp: DateTime.now());

Expand All @@ -249,16 +255,21 @@ class SendCryptoPresenter extends CompletePresenter<SendCryptoState> {
BottomFlowDialog.of(context!).close();
}
String errorMessage = e.message;
if (e.message.contains('gas required exceeds allowance')) {
errorMessage = translate('insufficient_balance_for_fee') ?? e.message;
}
errorMessage = changeErrorMessage(errorMessage);
addError(errorMessage);
}
} finally {
loading = false;
}
}

String changeErrorMessage(String message) {
if (message.contains('gas required exceeds allowance')) {
return translate('insufficient_balance_for_fee') ?? message;
}
return message;
}

@override
Future<void> dispose() async {
super.dispose();
Expand Down
Loading