mirror of
https://github.com/Such-Software/hash-wallet.git
synced 2026-10-17 05:41:29 +02:00
The big one. Touches 760+ files. Locks in the user's decision to break import-line compatibility with upstream cake-tech/cake_wallet in exchange for a binary named hash_wallet on every platform. Changes: - pubspec_description.yaml: project name cake_wallet -> hash_wallet - 762 Dart files: every 'package:cake_wallet/...' import rewritten to 'package:hash_wallet/...' (lib/, cw_*/lib/, tool/, test/, integration_test/) - linux/CMakeLists.txt: BINARY_NAME cake_wallet -> hash_wallet - windows/CMakeLists.txt: project + BINARY_NAME (CakeWallet -> HashWallet) - windows/runner/Runner.rc: company / file / product strings to Hash Wallet + Such Software LLC copyright - macos/Runner.xcodeproj/project_base.pbxproj: cake_wallet_new.app refs -> hash_wallet.app (macOS output bundle name) - com.suchsoftware.HashWallet.yml (Flatpak): all binary path refs cake_wallet -> hash_wallet (e.g. /app/hash_wallet/hash_wallet) - linux/com.suchsoftware.HashWallet.desktop: Exec=hash_wallet - scripts/android/copy_monero_deps.sh: CW_DIR target path - configure_hash_wallet.sh: error message typo (old filename) INTENTIONALLY UNCHANGED: - cw_* subpackage names (cw_core, cw_bitcoin, cw_wownero, etc.) — they remain as the historical 'cake wallet' naming; renaming them would cascade through every sub-package's internal structure - Method channel name 'com.cake_wallet/native_utils' in iOS/macOS/Android AppDelegates and MainActivity — it's a string identifier that has to match the Dart side exactly. Renaming requires both-sides change; it's internal-only and not user-visible. - ghcr.io/cake-tech/cake_wallet:debian13-... docker image references in build scripts — that's Cake Labs' published image we use as the build environment, not ours. User data directory now follows the new binary name: ~/.local/share/hash_wallet/ (was ~/.local/share/cake_wallet/) ~/.config/hash_wallet/ (was ~/.config/cake_wallet/) Existing users on the old name will need migration if any exist.
206 lines
6.5 KiB
Dart
206 lines
6.5 KiB
Dart
import 'package:hash_wallet/src/screens/wallet_connect/utils/string_parsing.dart';
|
|
|
|
class EVMTransactionErrorFeesHandler {
|
|
EVMTransactionErrorFeesHandler({
|
|
this.balanceWei,
|
|
this.balanceEth,
|
|
this.balanceUsd,
|
|
this.txCostWei,
|
|
this.txCostEth,
|
|
this.txCostUsd,
|
|
this.overshotWei,
|
|
this.overshotEth,
|
|
this.overshotUsd,
|
|
this.error,
|
|
});
|
|
|
|
String? balanceWei;
|
|
String? balanceEth;
|
|
String? balanceUsd;
|
|
|
|
String? txCostWei;
|
|
String? txCostEth;
|
|
String? txCostUsd;
|
|
|
|
String? overshotWei;
|
|
String? overshotEth;
|
|
String? overshotUsd;
|
|
|
|
String? error;
|
|
|
|
factory EVMTransactionErrorFeesHandler.parseEthereumFeesErrorMessage(
|
|
String errorMessage,
|
|
double assetPriceUsd,
|
|
) {
|
|
// Allows us define multiple patterns to parse the error message
|
|
// Order matters: more specific patterns first, generic patterns last
|
|
final patterns = [
|
|
// Pattern 1: "have X want Y" format
|
|
ErrorPattern(
|
|
name: 'have_want',
|
|
regex: RegExp(r'have\s+(\d+)\s+want\s+(\d+)', caseSensitive: false),
|
|
extractor: (match) => {
|
|
'balance': match.group(1)!,
|
|
'required': match.group(2)!,
|
|
},
|
|
calculator: (values) => {
|
|
'balanceWei': values['balance']!,
|
|
'txCostWei': values['required']!,
|
|
'overshotWei':
|
|
(BigInt.parse(values['required']!) - BigInt.parse(values['balance']!)).toString(),
|
|
},
|
|
),
|
|
|
|
// Pattern 2: "balance X, queued cost Y, tx cost Z, overshot W" format (most specific)
|
|
ErrorPattern(
|
|
name: 'balance_queued_txcost_overshot',
|
|
regex: RegExp(r'balance\s+(\d+),\s*queued\s+cost\s+(\d+),\s*tx\s+cost\s+(\d+),\s*overshot\s+(\d+)', caseSensitive: false),
|
|
extractor: (match) => {
|
|
'balance': match.group(1)!,
|
|
'txCost': match.group(3)!, // Skip queued cost, use tx cost
|
|
'overshot': match.group(4)!,
|
|
},
|
|
calculator: (values) => {
|
|
'balanceWei': values['balance']!,
|
|
'txCostWei': values['txCost']!,
|
|
'overshotWei': values['overshot']!,
|
|
},
|
|
),
|
|
|
|
// Pattern 3: "balance X, tx cost Y, overshot Z" format
|
|
ErrorPattern(
|
|
name: 'balance_txcost_overshot',
|
|
regex: RegExp(r'balance\s+(\d+),\s*tx\s+cost\s+(\d+),\s*overshot\s+(\d+)', caseSensitive: false),
|
|
extractor: (match) => {
|
|
'balance': match.group(1)!,
|
|
'txCost': match.group(2)!,
|
|
'overshot': match.group(3)!,
|
|
},
|
|
calculator: (values) => {
|
|
'balanceWei': values['balance']!,
|
|
'txCostWei': values['txCost']!,
|
|
'overshotWei': values['overshot']!,
|
|
},
|
|
),
|
|
|
|
// Pattern 4: Individual field matching (legacy fallback)
|
|
ErrorPattern(
|
|
name: 'individual_fields',
|
|
regex: RegExp(r'balance\s+(\d+).*tx\s+cost\s+(\d+).*overshot\s+(\d+)', caseSensitive: false),
|
|
extractor: (match) => {
|
|
'balance': match.group(1)!,
|
|
'txCost': match.group(2)!,
|
|
'overshot': match.group(3)!,
|
|
},
|
|
calculator: (values) => {
|
|
'balanceWei': values['balance']!,
|
|
'txCostWei': values['txCost']!,
|
|
'overshotWei': values['overshot']!,
|
|
},
|
|
),
|
|
|
|
// Pattern 5: Generic "insufficient funds for gas * price + value" (least specific - must be last)
|
|
ErrorPattern(
|
|
name: 'generic_insufficient_funds',
|
|
regex: RegExp(r'insufficient\s+funds\s+for\s+gas\s*\*\s*price\s*\+\s*value', caseSensitive: false),
|
|
extractor: (match) => {
|
|
'balance': '0', // We don't have specific values, so use 0
|
|
'txCost': '0',
|
|
'overshot': '0',
|
|
},
|
|
calculator: (values) => {
|
|
'balanceWei': values['balance']!,
|
|
'txCostWei': values['txCost']!,
|
|
'overshotWei': values['overshot']!,
|
|
},
|
|
),
|
|
];
|
|
|
|
for (final pattern in patterns) {
|
|
final match = pattern.regex.firstMatch(errorMessage);
|
|
if (match != null) {
|
|
try {
|
|
final extractedValues = pattern.extractor(match);
|
|
final calculatedValues = pattern.calculator(extractedValues);
|
|
|
|
return _createHandlerFromValues(calculatedValues, assetPriceUsd);
|
|
} catch (e) {
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
return EVMTransactionErrorFeesHandler(error: 'Could not parse the error message.');
|
|
}
|
|
|
|
/// Creates a handler instance from parsed values
|
|
static EVMTransactionErrorFeesHandler _createHandlerFromValues(
|
|
Map<String, String> values,
|
|
double assetPriceUsd,
|
|
) {
|
|
final balanceWei = BigInt.parse(values['balanceWei']!);
|
|
final txCostWei = BigInt.parse(values['txCostWei']!);
|
|
final overshotWei = BigInt.parse(values['overshotWei']!);
|
|
|
|
final isGenericError = balanceWei == BigInt.zero &&
|
|
txCostWei == BigInt.zero &&
|
|
overshotWei == BigInt.zero;
|
|
|
|
if (isGenericError) {
|
|
return genericInsufficientFunds();
|
|
}
|
|
|
|
// Convert wei to ETH (1 ETH = 1e18 wei)
|
|
final balanceEth = balanceWei.toDouble() / 1e18;
|
|
final txCostEth = txCostWei.toDouble() / 1e18;
|
|
final overshotEth = overshotWei.toDouble() / 1e18;
|
|
|
|
// Calculate USD values
|
|
final balanceUsd = balanceEth * assetPriceUsd;
|
|
final txCostUsd = txCostEth * assetPriceUsd;
|
|
final overshotUsd = overshotEth * assetPriceUsd;
|
|
|
|
return EVMTransactionErrorFeesHandler(
|
|
balanceWei: balanceWei.toString(),
|
|
balanceEth: balanceEth.toString().safeSubString(0, 12),
|
|
balanceUsd: balanceUsd.toString().safeSubString(0, 4),
|
|
txCostWei: txCostWei.toString(),
|
|
txCostEth: txCostEth.toString().safeSubString(0, 12),
|
|
txCostUsd: txCostUsd.toString().safeSubString(0, 4),
|
|
overshotWei: overshotWei.toString(),
|
|
overshotEth: overshotEth.toString().safeSubString(0, 12),
|
|
overshotUsd: overshotUsd.toString().safeSubString(0, 4),
|
|
);
|
|
}
|
|
|
|
static EVMTransactionErrorFeesHandler genericInsufficientFunds() {
|
|
return EVMTransactionErrorFeesHandler(
|
|
balanceWei: '0',
|
|
balanceEth: '0',
|
|
balanceUsd: '0',
|
|
txCostWei: '0',
|
|
txCostEth: '0',
|
|
txCostUsd: '0',
|
|
overshotWei: '0',
|
|
overshotEth: '0',
|
|
overshotUsd: '0',
|
|
error: 'generic_insufficient_funds',
|
|
);
|
|
}
|
|
}
|
|
|
|
/// Represents an error parsing pattern
|
|
class ErrorPattern {
|
|
final String name;
|
|
final RegExp regex;
|
|
final Map<String, String> Function(Match) extractor;
|
|
final Map<String, String> Function(Map<String, String>) calculator;
|
|
|
|
ErrorPattern({
|
|
required this.name,
|
|
required this.regex,
|
|
required this.extractor,
|
|
required this.calculator,
|
|
});
|
|
}
|