diff --git a/lib/common/utils/validation.dart b/lib/common/utils/validation.dart index 085f9895..b6ecc870 100644 --- a/lib/common/utils/validation.dart +++ b/lib/common/utils/validation.dart @@ -83,4 +83,9 @@ class Validation { return null; } } + + static bool isExpoNumber(String input) { + RegExp regex = RegExp(r'^(\d+\.\d+e[-+]\d+)$'); + return regex.hasMatch(input); + } } diff --git a/lib/features/common/contract/token_contract_use_case.dart b/lib/features/common/contract/token_contract_use_case.dart index a5e3a9ef..8ce13814 100644 --- a/lib/features/common/contract/token_contract_use_case.dart +++ b/lib/features/common/contract/token_contract_use_case.dart @@ -130,13 +130,14 @@ class TokenContractUseCase extends ReactiveUseCase { required String to, EtherAmount? gasPrice, Uint8List? data, + BigInt? amountOfGas, }) async => await _repository.tokenContract.estimateGesFee( - from: from, - to: to, - gasPrice: gasPrice, - data: data, - ); + from: from, + to: to, + gasPrice: gasPrice, + data: data, + amountOfGas: amountOfGas); Future sendTransaction({ required String privateKey, diff --git a/lib/features/dapps/subfeatures/open_dapp/open_dapp_presenter.dart b/lib/features/dapps/subfeatures/open_dapp/open_dapp_presenter.dart index 2a170e75..b5f3961d 100644 --- a/lib/features/dapps/subfeatures/open_dapp/open_dapp_presenter.dart +++ b/lib/features/dapps/subfeatures/open_dapp/open_dapp_presenter.dart @@ -55,15 +55,16 @@ class OpenDAppPresenter extends CompletePresenter { String to, EtherAmount? gasPrice, Uint8List? data, + BigInt? amountOfGas, ) async { loading = true; try { final gasFee = await _tokenContractUseCase.estimateGesFee( - from: from, - to: to, - gasPrice: gasPrice, - data: data, - ); + from: from, + to: to, + gasPrice: gasPrice, + data: data, + amountOfGas: amountOfGas); loading = false; return gasFee; @@ -74,18 +75,18 @@ class OpenDAppPresenter extends CompletePresenter { } } - Future _sendTransaction( - String to, EtherAmount amount, Uint8List? data, + Future _sendTransaction(String to, EtherAmount amount, + Uint8List? data, EstimatedGasFee? estimatedGasFee, {String? from}) async { loading = true; try { final res = await _tokenContractUseCase.sendTransaction( - privateKey: state.account!.privateKey, - to: to, - from: from, - amount: amount, - data: data, - ); + privateKey: state.account!.privateKey, + to: to, + from: from, + amount: amount, + data: data, + estimatedGasFee: estimatedGasFee); return res; } catch (e, s) { @@ -104,24 +105,26 @@ class OpenDAppPresenter extends CompletePresenter { final amount = amountEther.getValueInUnit(EtherUnit.ether).toString(); final bridgeData = hexToBytes(bridge.data ?? ''); EtherAmount? gasPrice; - EtherAmount? gasFee; + double? gasFee; EstimatedGasFee? estimatedGasFee; + BigInt? amountOfGas; if (bridge.gasPrice != null) { gasPrice = EtherAmount.fromBase10String(EtherUnit.wei, bridge.gasPrice!); } if (bridge.gas != null) { + amountOfGas = BigInt.parse(bridge.gas.toString()); gasPrice = gasPrice ?? await _tokenContractUseCase.getGasPrice(); - gasFee = EtherAmount.fromBigInt(EtherUnit.wei, - gasPrice.getInWei * BigInt.parse(bridge.gas.toString())); + final gasPriceDouble = + gasPrice.getValueInUnit(EtherUnit.ether).toDouble(); + gasFee = gasPriceDouble * amountOfGas.toDouble(); + + estimatedGasFee = + EstimatedGasFee(gasPrice: gasPrice, gas: amountOfGas, gasFee: gasFee); } else { estimatedGasFee = await _estimatedFee( - bridge.from!, - bridge.to!, - gasPrice, - bridgeData, - ); + bridge.from!, bridge.to!, gasPrice, bridgeData, amountOfGas); if (estimatedGasFee == null) { cancel.call(); @@ -129,19 +132,26 @@ class OpenDAppPresenter extends CompletePresenter { } } + String finalFee = estimatedGasFee.gasFee.toString(); + + if (Validation.isExpoNumber(finalFee)) { + finalFee = '0.0'; + } + + final symbol = state.network!.symbol; + try { - final result = await showTransactionDialog( - context!, - title: translate('confirm_transaction')!, - amount: amount, - from: bridge.from!, - to: bridge.to!, - estimatedFee: - '${gasFee?.getInWei != null ? gasFee!.getValueInUnit(EtherUnit.ether) : (estimatedGasFee?.gasFee ?? 0)}', - ); + final result = await showTransactionDialog(context!, + title: translate('confirm_transaction')!, + amount: amount, + from: bridge.from!, + to: bridge.to!, + estimatedFee: finalFee, + symbol: symbol); if (result != null && result) { - final hash = await _sendTransaction(bridge.to!, amountEther, bridgeData, + final hash = await _sendTransaction( + bridge.to!, amountEther, bridgeData, estimatedGasFee, from: bridge.from); if (hash != null) success.call(hash); } else { @@ -171,6 +181,7 @@ class OpenDAppPresenter extends CompletePresenter { }); } else { addError(translate('network_not_found')); + state.webviewController?.sendError(translate('network_not_found')!, id); } } @@ -188,6 +199,16 @@ class OpenDAppPresenter extends CompletePresenter { _chainConfigurationUseCase.switchDefaultNetwork(toNetwork); _authUseCase.resetNetwork(toNetwork); notify(() => state.network = toNetwork); - state.webviewController?.sendResult('null', id); + var config = """{ + ethereum: { + chainId: ${toNetwork.chainId}, + rpcUrl: "${toNetwork.web3RpcHttpUrl}", + address: "${state.account!.address}", + isDebug: true, + networkVersion: "${toNetwork.chainId}", + isMetaMask: true + } + }"""; + state.webviewController?.setChain(config, toNetwork.chainId, id); } } diff --git a/lib/features/dapps/subfeatures/open_dapp/widgets/transaction_dialog.dart b/lib/features/dapps/subfeatures/open_dapp/widgets/transaction_dialog.dart index 768a1ee8..b6da7c4d 100644 --- a/lib/features/dapps/subfeatures/open_dapp/widgets/transaction_dialog.dart +++ b/lib/features/dapps/subfeatures/open_dapp/widgets/transaction_dialog.dart @@ -11,6 +11,7 @@ Future showTransactionDialog( required String to, String? estimatedFee, VoidCallback? onTap, + required String symbol }) { return showModalBottomSheet( context: context, @@ -46,6 +47,7 @@ Future showTransactionDialog( to: to, estimatedFee: estimatedFee, onTap: onTap, + symbol: symbol, ), const SizedBox(height: 10), ], diff --git a/lib/features/dapps/subfeatures/open_dapp/widgets/transaction_info.dart b/lib/features/dapps/subfeatures/open_dapp/widgets/transaction_info.dart index f6218b87..268b398c 100644 --- a/lib/features/dapps/subfeatures/open_dapp/widgets/transaction_info.dart +++ b/lib/features/dapps/subfeatures/open_dapp/widgets/transaction_info.dart @@ -7,20 +7,22 @@ import 'package:mxc_ui/mxc_ui.dart'; import 'transaction_dialog.dart'; class TransactionInfo extends StatelessWidget { - const TransactionInfo({ - Key? key, - required this.amount, - required this.from, - required this.to, - this.estimatedFee, - this.onTap, - }) : super(key: key); + const TransactionInfo( + {Key? key, + required this.amount, + required this.from, + required this.to, + this.estimatedFee, + this.onTap, + required this.symbol}) + : super(key: key); final String amount; final String from; final String to; final String? estimatedFee; final VoidCallback? onTap; + final String symbol; @override Widget build(BuildContext context) { @@ -80,7 +82,7 @@ class TransactionInfo extends StatelessWidget { ), const SizedBox(width: 4), Text( - 'MXC', + symbol, style: FontTheme.of(context).h5.secondary(), ), const SizedBox(height: 4), @@ -110,7 +112,7 @@ class TransactionInfo extends StatelessWidget { ), const SizedBox(width: 4), Text( - 'MXC', + symbol, style: FontTheme.of(context).body1().copyWith( color: ColorsTheme.of(context).grey2, ), diff --git a/lib/features/portfolio/subfeatures/nft/send_nft/send_nft_presenter.dart b/lib/features/portfolio/subfeatures/nft/send_nft/send_nft_presenter.dart index 7a20e35f..232ffa82 100644 --- a/lib/features/portfolio/subfeatures/nft/send_nft/send_nft_presenter.dart +++ b/lib/features/portfolio/subfeatures/nft/send_nft/send_nft_presenter.dart @@ -20,6 +20,8 @@ class SendNftPresenter extends CompletePresenter { late final _tokenContractUseCase = ref.read(tokenContractUseCaseProvider); late final _nftContractUseCase = ref.read(nftContractUseCaseProvider); late final _accountUseCase = ref.read(accountUseCaseProvider); + late final _chainConfigurationUseCase = + ref.read(chainConfigurationUseCaseProvider); late final _nftsUseCase = ref.read(nftsUseCaseProvider); late final TextEditingController recipientController = TextEditingController(); @@ -36,6 +38,14 @@ class SendNftPresenter extends CompletePresenter { }, ); + listen( + _chainConfigurationUseCase.selectedNetwork, + (value) { + notify(() => state.network = value); + loadPage(); + }, + ); + listen( _nftContractUseCase.online, (value) => notify(() => state.online = value), @@ -57,17 +67,18 @@ class SendNftPresenter extends CompletePresenter { } } - final result = await showTransactionDialog( - context!, - title: _getDialogTitle(nft.name), - nft: nft, - newtork: 'MXC zkEVM', - from: state.account!.address, - to: recipient, - processType: state.processType, - estimatedFee: state.estimatedGasFee?.gasFee.toString(), - onTap: _nextTransactionStep, - ); + final symbol = state.network!.symbol; + + final result = await showTransactionDialog(context!, + title: _getDialogTitle(nft.name), + nft: nft, + newtork: 'MXC zkEVM', + from: state.account!.address, + to: recipient, + processType: state.processType, + estimatedFee: state.estimatedGasFee?.gasFee.toString(), + onTap: _nextTransactionStep, + symbol: symbol); if (result != null && !result) { notify(() => state.processType = TransactionProcessType.confirm); diff --git a/lib/features/portfolio/subfeatures/nft/send_nft/send_nft_state.dart b/lib/features/portfolio/subfeatures/nft/send_nft/send_nft_state.dart index 32926bfb..fb71d12c 100644 --- a/lib/features/portfolio/subfeatures/nft/send_nft/send_nft_state.dart +++ b/lib/features/portfolio/subfeatures/nft/send_nft/send_nft_state.dart @@ -11,6 +11,7 @@ class SendNftState with EquatableMixin { TransactionProcessType processType = TransactionProcessType.confirm; Account? account; EstimatedGasFee? estimatedGasFee; + Network? network; @override List get props => [ diff --git a/lib/features/portfolio/subfeatures/nft/send_nft/widgets/transaction_dialog.dart b/lib/features/portfolio/subfeatures/nft/send_nft/widgets/transaction_dialog.dart index cdcd8e12..d97811bc 100644 --- a/lib/features/portfolio/subfeatures/nft/send_nft/widgets/transaction_dialog.dart +++ b/lib/features/portfolio/subfeatures/nft/send_nft/widgets/transaction_dialog.dart @@ -6,17 +6,16 @@ import 'transaction_info.dart'; enum TransactionProcessType { confirm, send, done } -Future showTransactionDialog( - BuildContext context, { - String? title, - required Nft nft, - required String newtork, - required String from, - required String to, - String? estimatedFee, - TransactionProcessType? processType, - VoidCallback? onTap, -}) { +Future showTransactionDialog(BuildContext context, + {String? title, + required Nft nft, + required String newtork, + required String from, + required String to, + String? estimatedFee, + TransactionProcessType? processType, + VoidCallback? onTap, + required String symbol}) { return showModalBottomSheet( context: context, useRootNavigator: true, @@ -52,6 +51,7 @@ Future showTransactionDialog( estimatedFee: estimatedFee, processType: processType, onTap: onTap, + symbol: symbol, ), const SizedBox(height: 10), ], diff --git a/lib/features/portfolio/subfeatures/nft/send_nft/widgets/transaction_info.dart b/lib/features/portfolio/subfeatures/nft/send_nft/widgets/transaction_info.dart index 6f80b5b0..8a03126f 100644 --- a/lib/features/portfolio/subfeatures/nft/send_nft/widgets/transaction_info.dart +++ b/lib/features/portfolio/subfeatures/nft/send_nft/widgets/transaction_info.dart @@ -8,16 +8,17 @@ import 'package:mxc_ui/mxc_ui.dart'; import 'transaction_dialog.dart'; class TransactionInfo extends StatelessWidget { - const TransactionInfo({ - Key? key, - required this.nft, - required this.newtork, - required this.from, - required this.to, - this.estimatedFee, - this.processType = TransactionProcessType.confirm, - this.onTap, - }) : super(key: key); + const TransactionInfo( + {Key? key, + required this.nft, + required this.newtork, + required this.from, + required this.to, + this.estimatedFee, + this.processType = TransactionProcessType.confirm, + this.onTap, + required this.symbol}) + : super(key: key); final Nft nft; final String newtork; @@ -25,6 +26,7 @@ class TransactionInfo extends StatelessWidget { final String to; final String? estimatedFee; final TransactionProcessType? processType; + final String symbol; final VoidCallback? onTap; @override @@ -39,7 +41,7 @@ class TransactionInfo extends StatelessWidget { addressItem(context, 'from', from), addressItem(context, 'to', to), if (TransactionProcessType.confirm != processType) - priceItem(context, 'estimated_fee', estimatedFee), + priceItem(context, 'estimated_fee', estimatedFee, symbol), ], ), ), @@ -92,10 +94,7 @@ class TransactionInfo extends StatelessWidget { } Widget priceItem( - BuildContext context, - String label, - String? price, - ) { + BuildContext context, String label, String? price, String symbol) { return TransactionItem( label: label, content: Row( @@ -103,7 +102,9 @@ class TransactionInfo extends StatelessWidget { children: [ Text( price != null - ? Formatter.formatNumberForUI(price,) + ? Formatter.formatNumberForUI( + price, + ) : '--', style: FontTheme.of(context).body1.primary(), ), diff --git a/packages/shared b/packages/shared index 398b14b9..95006188 160000 --- a/packages/shared +++ b/packages/shared @@ -1 +1 @@ -Subproject commit 398b14b957dc481e2369604b0733da3cd5f24055 +Subproject commit 95006188a3c7b06fe575aab2b5d9e252c2a2b1d5 diff --git a/packages/web3_provider/lib/js_bridge_callback_bean.dart b/packages/web3_provider/lib/js_bridge_callback_bean.dart index c213c364..9a5ac3aa 100644 --- a/packages/web3_provider/lib/js_bridge_callback_bean.dart +++ b/packages/web3_provider/lib/js_bridge_callback_bean.dart @@ -38,4 +38,13 @@ extension Web3Result on InAppWebViewController { await evaluateJavascript(source: script); sendResults([address], id); } + + void setChain(String config, int chainId, int id) async { + final setConfigScript = "console.log(window.ethereum.setConfig($config))"; + final emitChainChangeScript = + "console.log(window.ethereum.emitChainChanged($chainId))"; + await evaluateJavascript(source: setConfigScript); + await evaluateJavascript(source: emitChainChangeScript); + sendResult('null', id); + } } diff --git a/pubspec.yaml b/pubspec.yaml index d0f2852d..606ec8b3 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -16,7 +16,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # In Windows, build-name is used as the major, minor, and patch parts # of the product and file versions while build-number is used as the build suffix. -version: 1.2.1 +version: 1.2.2 environment: sdk: ">=2.19.0 <3.0.0"