mirror of
https://github.com/cake-tech/cake_wallet.git
synced 2025-06-28 04:19:50 +00:00
Deuro Savings Error Handling (#2340)
* feat(deuro): Enhance gas fee handling and error management for Deuro Savings Transactions. This change: - Introduces DeuroGasFeeException to handle insufficient ETH for gas fees. - Adds check for ETH balance before savings transactions to prevent failures due to insufficient funds. - Updates savings transaction methods to include error handling. - Adds UI feedback for transaction failures in DEuroSavingsPage. * Fix conflicts * Update cw_ethereum/lib/deuro/deuro_savings.dart Co-authored-by: Konstantin Ullrich <konstantinullrich12@gmail.com> --------- Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> Co-authored-by: Konstantin Ullrich <konstantinullrich12@gmail.com>
This commit is contained in:
parent
b3c20a5818
commit
5aeb6b7522
4 changed files with 186 additions and 59 deletions
|
@ -2,6 +2,7 @@ import 'package:cw_core/crypto_currency.dart';
|
|||
import 'package:cw_ethereum/deuro/deuro_savings_contract.dart';
|
||||
import 'package:cw_ethereum/ethereum_wallet.dart';
|
||||
import 'package:cw_evm/contract/erc20.dart';
|
||||
import 'package:cw_evm/evm_chain_exceptions.dart';
|
||||
import 'package:cw_evm/evm_chain_transaction_priority.dart';
|
||||
import 'package:cw_evm/pending_evm_chain_transaction.dart';
|
||||
import 'package:web3dart/crypto.dart';
|
||||
|
@ -43,8 +44,35 @@ class DEuro {
|
|||
|
||||
Future<BigInt> get approvedBalance => _dEuro.allowance(_address, _savingsGateway.self.address);
|
||||
|
||||
Future<void> _checkEthBalanceForGasFees(EVMChainTransactionPriority priority) async {
|
||||
final ethBalance = await _wallet.getWeb3Client()!.getBalance(_address);
|
||||
final currentBalance = ethBalance.getInWei;
|
||||
|
||||
final gasFeesModel = await _wallet.calculateActualEstimatedFeeForCreateTransaction(
|
||||
amount: BigInt.zero,
|
||||
contractAddress: _savingsGateway.self.address.hexEip55,
|
||||
receivingAddressHex: _savingsGateway.self.address.hexEip55,
|
||||
priority: priority,
|
||||
data: _savingsGateway.self.abi.functions[17]
|
||||
.encodeCall([BigInt.zero, hexToBytes(frontendCode)]),
|
||||
);
|
||||
|
||||
final estimatedGasFee = BigInt.from(gasFeesModel.estimatedGasFee);
|
||||
final requiredBalance = estimatedGasFee;
|
||||
|
||||
if (currentBalance < requiredBalance) {
|
||||
throw DeuroGasFeeException(
|
||||
requiredGasFee: requiredBalance,
|
||||
currentBalance: currentBalance,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<PendingEVMChainTransaction> depositSavings(
|
||||
BigInt amount, EVMChainTransactionPriority priority) async {
|
||||
try {
|
||||
await _checkEthBalanceForGasFees(priority);
|
||||
|
||||
final signedTransaction = await _savingsGateway.save(
|
||||
(amount: amount, frontendCode: hexToBytes(frontendCode)),
|
||||
credentials: _wallet.evmChainPrivateKey,
|
||||
|
@ -65,11 +93,24 @@ class DEuro {
|
|||
signedTransaction: signedTransaction,
|
||||
fee: BigInt.from(fee.estimatedGasFee),
|
||||
amount: amount.toString(),
|
||||
exponent: 18);
|
||||
exponent: 18,
|
||||
);
|
||||
} catch (e) {
|
||||
if (e.toString().contains('insufficient funds for gas')) {
|
||||
final ethBalance = await _wallet.getWeb3Client()!.getBalance(_address);
|
||||
throw DeuroGasFeeException(
|
||||
currentBalance: ethBalance.getInWei,
|
||||
);
|
||||
}
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
Future<PendingEVMChainTransaction> withdrawSavings(
|
||||
BigInt amount, EVMChainTransactionPriority priority) async {
|
||||
try {
|
||||
await _checkEthBalanceForGasFees(priority);
|
||||
|
||||
final signedTransaction = await _savingsGateway.withdraw(
|
||||
(target: _address, amount: amount, frontendCode: hexToBytes(frontendCode)),
|
||||
credentials: _wallet.evmChainPrivateKey,
|
||||
|
@ -91,11 +132,23 @@ class DEuro {
|
|||
fee: BigInt.from(fee.estimatedGasFee),
|
||||
amount: amount.toString(),
|
||||
exponent: 18);
|
||||
} catch (e) {
|
||||
if (e.toString().contains('insufficient funds for gas')) {
|
||||
final ethBalance = await _wallet.getWeb3Client()!.getBalance(_address);
|
||||
throw DeuroGasFeeException(
|
||||
currentBalance: ethBalance.getInWei,
|
||||
);
|
||||
}
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
// Set an infinite approval to save gas in the future
|
||||
Future<PendingEVMChainTransaction> enableSavings(EVMChainTransactionPriority priority) async =>
|
||||
(await _wallet.createApprovalTransaction(
|
||||
Future<PendingEVMChainTransaction> enableSavings(EVMChainTransactionPriority priority) async {
|
||||
try {
|
||||
await _checkEthBalanceForGasFees(priority);
|
||||
|
||||
return (await _wallet.createApprovalTransaction(
|
||||
BigInt.parse(
|
||||
'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
|
||||
radix: 16,
|
||||
|
@ -104,4 +157,14 @@ class DEuro {
|
|||
CryptoCurrency.deuro,
|
||||
priority,
|
||||
)) as PendingEVMChainTransaction;
|
||||
} catch (e) {
|
||||
if (e.toString().contains('insufficient funds for gas')) {
|
||||
final ethBalance = await _wallet.getWeb3Client()!.getBalance(_address);
|
||||
throw DeuroGasFeeException(
|
||||
currentBalance: ethBalance.getInWei,
|
||||
);
|
||||
}
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,3 +22,32 @@ class EVMChainTransactionFeesException implements Exception {
|
|||
@override
|
||||
String toString() => exceptionMessage;
|
||||
}
|
||||
|
||||
class DeuroGasFeeException implements Exception {
|
||||
final String exceptionMessage;
|
||||
final BigInt? requiredGasFee;
|
||||
final BigInt? currentBalance;
|
||||
|
||||
DeuroGasFeeException({
|
||||
this.requiredGasFee,
|
||||
this.currentBalance,
|
||||
}) : exceptionMessage = _buildMessage(requiredGasFee, currentBalance);
|
||||
|
||||
static String _buildMessage(BigInt? requiredGasFee, BigInt? currentBalance) {
|
||||
const baseMessage = 'Insufficient ETH for gas fees.';
|
||||
const addEthMessage = ' Please add ETH to your wallet to cover transaction fees.';
|
||||
|
||||
if (requiredGasFee != null) {
|
||||
final requiredEth = (requiredGasFee / BigInt.from(10).pow(18)).toStringAsFixed(8);
|
||||
final balanceInfo = currentBalance != null
|
||||
? ', Available: ${(currentBalance / BigInt.from(10).pow(18)).toStringAsFixed(8)} ETH'
|
||||
: '';
|
||||
return '$baseMessage Required: ~$requiredEth ETH$balanceInfo.$addEthMessage';
|
||||
}
|
||||
|
||||
return '$baseMessage$addEthMessage';
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() => exceptionMessage;
|
||||
}
|
||||
|
|
|
@ -4,9 +4,11 @@ import 'package:cake_wallet/src/screens/base_page.dart';
|
|||
import 'package:cake_wallet/src/screens/integrations/deuro/widgets/interest_card_widget.dart';
|
||||
import 'package:cake_wallet/src/screens/integrations/deuro/widgets/savings_card_widget.dart';
|
||||
import 'package:cake_wallet/src/screens/integrations/deuro/widgets/savings_edit_sheet.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
|
||||
import 'package:cake_wallet/src/widgets/bottom_sheet/confirm_sending_bottom_sheet_widget.dart';
|
||||
import 'package:cake_wallet/src/widgets/bottom_sheet/info_bottom_sheet_widget.dart';
|
||||
import 'package:cake_wallet/src/widgets/gradient_background.dart';
|
||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||
import 'package:cake_wallet/view_model/integrations/deuro_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/send/send_view_model_state.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
|
@ -190,6 +192,24 @@ class DEuroSavingsPage extends BasePage {
|
|||
);
|
||||
});
|
||||
}
|
||||
|
||||
if (state is FailureState) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||
if (!context.mounted) return;
|
||||
|
||||
await showPopUp<void>(
|
||||
context: context,
|
||||
builder: (BuildContext popupContext) {
|
||||
return AlertWithOneAction(
|
||||
alertTitle: S.of(popupContext).error,
|
||||
alertContent: state.error,
|
||||
buttonText: S.of(popupContext).ok,
|
||||
buttonAction: () => Navigator.of(popupContext).pop(),
|
||||
);
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
_isReactionsSet = true;
|
||||
|
|
|
@ -46,10 +46,8 @@ abstract class DEuroViewModelBase with Store {
|
|||
|
||||
@action
|
||||
Future<void> reloadSavingsUserData() async {
|
||||
final savingsBalanceRaw =
|
||||
ethereum!.getDEuroSavingsBalance(_appStore.wallet!);
|
||||
final accruedInterestRaw =
|
||||
ethereum!.getDEuroAccruedInterest(_appStore.wallet!);
|
||||
final savingsBalanceRaw = ethereum!.getDEuroSavingsBalance(_appStore.wallet!);
|
||||
final accruedInterestRaw = ethereum!.getDEuroAccruedInterest(_appStore.wallet!);
|
||||
|
||||
approvedTokens = await ethereum!.getDEuroSavingsApproved(_appStore.wallet!);
|
||||
|
||||
|
@ -63,56 +61,73 @@ abstract class DEuroViewModelBase with Store {
|
|||
|
||||
@action
|
||||
Future<void> reloadInterestRate() async {
|
||||
final interestRateRaw =
|
||||
await ethereum!.getDEuroInterestRate(_appStore.wallet!);
|
||||
final interestRateRaw = await ethereum!.getDEuroInterestRate(_appStore.wallet!);
|
||||
|
||||
interestRate = (interestRateRaw / BigInt.from(10000)).toString();
|
||||
}
|
||||
|
||||
@action
|
||||
Future<void> prepareApproval() async {
|
||||
try {
|
||||
state = TransactionCommitting();
|
||||
final priority = _appStore.settingsStore.priority[WalletType.ethereum]!;
|
||||
approvalTransaction =
|
||||
await ethereum!.enableDEuroSaving(_appStore.wallet!, priority);
|
||||
approvalTransaction = await ethereum!.enableDEuroSaving(_appStore.wallet!, priority);
|
||||
state = InitialExecutionState();
|
||||
} catch (e) {
|
||||
state = FailureState(e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@action
|
||||
Future<void> prepareSavingsEdit(String amountRaw, bool isAdding) async {
|
||||
try {
|
||||
state = TransactionCommitting();
|
||||
final amount = BigInt.from(num.parse(amountRaw) * pow(10, 18));
|
||||
final priority = _appStore.settingsStore.priority[WalletType.ethereum]!;
|
||||
transaction = await (isAdding
|
||||
? ethereum!.addDEuroSaving(_appStore.wallet!, amount, priority)
|
||||
: ethereum!.removeDEuroSaving(_appStore.wallet!, amount, priority));
|
||||
state = InitialExecutionState();
|
||||
} catch (e) {
|
||||
state = FailureState(e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> prepareCollectInterest() =>
|
||||
prepareSavingsEdit(accruedInterest, false);
|
||||
Future<void> prepareCollectInterest() => prepareSavingsEdit(accruedInterest, false);
|
||||
|
||||
@action
|
||||
Future<void> commitTransaction() async {
|
||||
if (transaction != null) {
|
||||
try {
|
||||
state = TransactionCommitting();
|
||||
await transaction!.commit();
|
||||
transaction = null;
|
||||
reloadSavingsUserData();
|
||||
state = TransactionCommitted();
|
||||
} catch (e) {
|
||||
state = FailureState(e.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@action
|
||||
Future<void> commitApprovalTransaction() async {
|
||||
if (approvalTransaction != null) {
|
||||
try {
|
||||
state = TransactionCommitting();
|
||||
await approvalTransaction!.commit();
|
||||
approvalTransaction = null;
|
||||
reloadSavingsUserData();
|
||||
state = TransactionCommitted();
|
||||
} catch (e) {
|
||||
state = FailureState(e.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@action
|
||||
void dismissTransaction() {
|
||||
transaction == null;
|
||||
transaction = null;
|
||||
approvalTransaction = null;
|
||||
state = InitialExecutionState();
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue