diff --git a/lib/app/shared/extension/double_extension.dart b/lib/app/shared/extension/double_extension.dart index d4769a860..aa3a7556e 100644 --- a/lib/app/shared/extension/double_extension.dart +++ b/lib/app/shared/extension/double_extension.dart @@ -2,6 +2,9 @@ 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; } diff --git a/lib/app/shared/extension/string_extension.dart b/lib/app/shared/extension/string_extension.dart index 1af43fb9c..422fed68e 100644 --- a/lib/app/shared/extension/string_extension.dart +++ b/lib/app/shared/extension/string_extension.dart @@ -1,6 +1,7 @@ import 'dart:convert'; import 'package:convert/convert.dart'; +import 'package:decimal/decimal.dart'; import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; @@ -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/m_web3_client/m_web3_client.dart b/lib/app/shared/m_web3_client/m_web3_client.dart index d21d7de9f..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,7 +4,6 @@ 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 { @@ -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/dashboard/connection/operation/cubit/operation_cubit.dart b/lib/dashboard/connection/operation/cubit/operation_cubit.dart index 5b25b052e..97b122408 100644 --- a/lib/dashboard/connection/operation/cubit/operation_cubit.dart +++ b/lib/dashboard/connection/operation/cubit/operation_cubit.dart @@ -212,7 +212,7 @@ class OperationCubit extends Cubit { 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!, @@ -353,8 +353,7 @@ class OperationCubit extends Cubit { 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/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 5b9c5cf5a..2f3e8e29d 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 @@ -8,6 +8,7 @@ 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'; @@ -93,7 +94,12 @@ class ConfirmTokenTransactionCubit extends Cubit { } } + BigInt? gas; + EtherAmount? gasPrice; + Future _calculateEVMFee() async { + gas = null; + gasPrice = null; final blockchainType = walletCubit.state.currentAccount?.blockchainType; if (blockchainType == null) { @@ -105,10 +111,9 @@ class ConfirmTokenTransactionCubit extends Cubit { ); } - final amount = state.tokenAmount - .toStringAsFixed(int.parse(state.selectedToken.decimals)) - .replaceAll(',', '') - .replaceAll('.', ''); + final amount = + Decimal.parse(state.tokenAmount.replaceAll('.', '').replaceAll(',', '')) + .toBigInt(); final credentials = EthPrivateKey.fromHex(state.selectedAccountSecretKey); final sender = credentials.address; @@ -116,18 +121,19 @@ class ConfirmTokenTransactionCubit extends Cubit { final web3RpcURL = await fetchRpcUrl(manageNetworkCubit.state.network); - final maxGas = await MWeb3Client.estimateEthereumFee( + 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); + gas = maxGas; + gasPrice = priceOfGas; + + final fee = EtherAmount.inWei(feeData).getValueInUnit(EtherUnit.ether); final etherUSDPrice = (await getEVMUSDPrice(blockchainType)) ?? 0; final double feeInUSD = etherUSDPrice * fee; @@ -144,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, ), ); @@ -205,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, ), @@ -242,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)}}'''; @@ -263,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); @@ -310,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( @@ -322,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; } @@ -344,49 +357,92 @@ 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 = BigInt.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(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(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, @@ -416,37 +472,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; - - final chainRpcUrl = await fetchRpcUrl(manageNetworkCubit.state.network); - - await _sendContractInvocationOperationEthereum( - tokenAmount: state.totalAmount, - selectedAccountSecretKey: state.selectedAccountSecretKey, - token: state.selectedToken, - chainId: (selectedEthereumNetwork as EthereumNetwork).chainId, - chainRpcUrl: chainRpcUrl, - rpcUrl: chainRpcUrl, - ); - } - } catch (e, s) { - logger.e('e: $e s: $s'); - rethrow; - } - } - Future _sendContractInvocationOperationTezos({ required double tokenAmount, required String selectedAccountSecretKey, @@ -547,8 +572,8 @@ class ConfirmTokenTransactionCubit extends Cubit { } } - Future _sendContractInvocationOperationEthereum({ - required double tokenAmount, + Future _sendContractInvocationOperationEVM({ + required String tokenAmount, required String selectedAccountSecretKey, required TokenModel token, required int chainId, @@ -588,7 +613,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('.', ''), ); @@ -615,28 +640,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 d08525ae2..cf2f30eef 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 @@ -81,7 +82,7 @@ class ConfirmWithdrawalView extends StatefulWidget { final TokenModel selectedToken; final String withdrawalAddress; - final double amount; + final String amount; final bool isNFT; @override @@ -129,7 +130,7 @@ class _ConfirmWithdrawalViewState extends State { } if (state.status == AppStatus.success) { final amountAndSymbol = - '''${widget.isNFT ? widget.amount.toInt() : state.totalAmount.decimalNumber(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, @@ -151,7 +152,7 @@ class _ConfirmWithdrawalViewState extends State { }, builder: (context, state) { final amountAndSymbol = - '''${widget.isNFT ? widget.amount.toInt() : state.tokenAmount.decimalNumber(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, @@ -166,17 +167,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 075178224..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,13 +49,13 @@ class ConfirmTransactionDetailsCard extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.end, children: [ Text( - '''${isNFT ? grandTotal.toInt() : grandTotal.decimalNumber(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) + (double.parse(grandTotal) * tokenUSDRate) .decimalNumber(2) .formatNumber, style: Theme.of(context).textTheme.bodySmall2, @@ -109,13 +110,15 @@ class ConfirmTransactionDetailsCard extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.end, children: [ Text( - '''${isNFT ? amount.toInt() : amount.decimalNumber(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).decimalNumber(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 900ffef98..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'; @@ -19,12 +20,13 @@ class InsertWithdrawalPageCubit extends Cubit { 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), ), ); } @@ -33,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 058cf76d8..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,25 +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, + String? validAmount, + String? insertedAmount, }) { return TokenAmountCalculatorState( - amount: amount ?? this.amount, insertedAmount: insertedAmount ?? this.insertedAmount, validAmount: validAmount ?? this.validAmount, ); @@ -31,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/widgets/token_amount_calculator.dart b/lib/dashboard/home/tab_bar/tokens/insert_withdrawal_amount/widgets/token_amount_calculator.dart index c03280906..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 @@ -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/pubspec.yaml b/pubspec.yaml index ee5249790..d85861ace 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: altme description: AltMe Flutter App -version: 2.4.24+444 +version: 2.4.26+446 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