dev
dart 209 lines 6.52 KB
Raw
1 import 'package:cake_wallet/src/screens/wallet_connect/utils/string_parsing.dart';
2
3 class EVMTransactionErrorFeesHandler {
4 EVMTransactionErrorFeesHandler({
5 this.balanceWei,
6 this.balanceEth,
7 this.balanceUsd,
8 this.txCostWei,
9 this.txCostEth,
10 this.txCostUsd,
11 this.overshotWei,
12 this.overshotEth,
13 this.overshotUsd,
14 this.error,
15 });
16
17 String? balanceWei;
18 String? balanceEth;
19 String? balanceUsd;
20
21 String? txCostWei;
22 String? txCostEth;
23 String? txCostUsd;
24
25 String? overshotWei;
26 String? overshotEth;
27 String? overshotUsd;
28
29 String? error;
30
31 factory EVMTransactionErrorFeesHandler.parseEthereumFeesErrorMessage(
32 String errorMessage,
33 double assetPriceUsd,
34 ) {
35 // Allows us define multiple patterns to parse the error message
36 // Order matters: more specific patterns first, generic patterns last
37 final patterns = [
38 // Pattern 1: "have X want Y" format
39 ErrorPattern(
40 name: 'have_want',
41 regex: RegExp(r'have\s+(\d+)\s+want\s+(\d+)', caseSensitive: false),
42 extractor: (match) => {
43 'balance': match.group(1)!,
44 'required': match.group(2)!,
45 },
46 calculator: (values) => {
47 'balanceWei': values['balance']!,
48 'txCostWei': values['required']!,
49 'overshotWei':
50 (BigInt.parse(values['required']!) - BigInt.parse(values['balance']!)).toString(),
51 },
52 ),
53
54 // Pattern 2: "balance X, queued cost Y, tx cost Z, overshot W" format (most specific)
55 ErrorPattern(
56 name: 'balance_queued_txcost_overshot',
57 regex: RegExp(
58 r'balance\s+(\d+),\s*queued\s+cost\s+(\d+),\s*tx\s+cost\s+(\d+),\s*overshot\s+(\d+)',
59 caseSensitive: false),
60 extractor: (match) => {
61 'balance': match.group(1)!,
62 'txCost': match.group(3)!, // Skip queued cost, use tx cost
63 'overshot': match.group(4)!,
64 },
65 calculator: (values) => {
66 'balanceWei': values['balance']!,
67 'txCostWei': values['txCost']!,
68 'overshotWei': values['overshot']!,
69 },
70 ),
71
72 // Pattern 3: "balance X, tx cost Y, overshot Z" format
73 ErrorPattern(
74 name: 'balance_txcost_overshot',
75 regex: RegExp(r'balance\s+(\d+),\s*tx\s+cost\s+(\d+),\s*overshot\s+(\d+)',
76 caseSensitive: false),
77 extractor: (match) => {
78 'balance': match.group(1)!,
79 'txCost': match.group(2)!,
80 'overshot': match.group(3)!,
81 },
82 calculator: (values) => {
83 'balanceWei': values['balance']!,
84 'txCostWei': values['txCost']!,
85 'overshotWei': values['overshot']!,
86 },
87 ),
88
89 // Pattern 4: Individual field matching (legacy fallback)
90 ErrorPattern(
91 name: 'individual_fields',
92 regex:
93 RegExp(r'balance\s+(\d+).*tx\s+cost\s+(\d+).*overshot\s+(\d+)', caseSensitive: false),
94 extractor: (match) => {
95 'balance': match.group(1)!,
96 'txCost': match.group(2)!,
97 'overshot': match.group(3)!,
98 },
99 calculator: (values) => {
100 'balanceWei': values['balance']!,
101 'txCostWei': values['txCost']!,
102 'overshotWei': values['overshot']!,
103 },
104 ),
105
106 // Pattern 5: Generic "insufficient funds for gas * price + value" (least specific - must be last)
107 ErrorPattern(
108 name: 'generic_insufficient_funds',
109 regex: RegExp(r'insufficient\s+funds\s+for\s+gas\s*\*\s*price\s*\+\s*value',
110 caseSensitive: false),
111 extractor: (match) => {
112 'balance': '0', // We don't have specific values, so use 0
113 'txCost': '0',
114 'overshot': '0',
115 },
116 calculator: (values) => {
117 'balanceWei': values['balance']!,
118 'txCostWei': values['txCost']!,
119 'overshotWei': values['overshot']!,
120 },
121 ),
122 ];
123
124 for (final pattern in patterns) {
125 final match = pattern.regex.firstMatch(errorMessage);
126 if (match != null) {
127 try {
128 final extractedValues = pattern.extractor(match);
129 final calculatedValues = pattern.calculator(extractedValues);
130
131 return _createHandlerFromValues(calculatedValues, assetPriceUsd);
132 } catch (e) {
133 continue;
134 }
135 }
136 }
137
138 return EVMTransactionErrorFeesHandler(error: 'Could not parse the error message.');
139 }
140
141 /// Creates a handler instance from parsed values
142 static EVMTransactionErrorFeesHandler _createHandlerFromValues(
143 Map<String, String> values,
144 double assetPriceUsd,
145 ) {
146 final balanceWei = BigInt.parse(values['balanceWei']!);
147 final txCostWei = BigInt.parse(values['txCostWei']!);
148 final overshotWei = BigInt.parse(values['overshotWei']!);
149
150 final isGenericError =
151 balanceWei == BigInt.zero && txCostWei == BigInt.zero && overshotWei == BigInt.zero;
152
153 if (isGenericError) {
154 return genericInsufficientFunds();
155 }
156
157 // Convert wei to ETH (1 ETH = 1e18 wei)
158 final balanceEth = balanceWei.toDouble() / 1e18;
159 final txCostEth = txCostWei.toDouble() / 1e18;
160 final overshotEth = overshotWei.toDouble() / 1e18;
161
162 // Calculate USD values
163 final balanceUsd = balanceEth * assetPriceUsd;
164 final txCostUsd = txCostEth * assetPriceUsd;
165 final overshotUsd = overshotEth * assetPriceUsd;
166
167 return EVMTransactionErrorFeesHandler(
168 balanceWei: balanceWei.toString(),
169 balanceEth: balanceEth.toString().safeSubString(0, 12),
170 balanceUsd: balanceUsd.toString().safeSubString(0, 4),
171 txCostWei: txCostWei.toString(),
172 txCostEth: txCostEth.toString().safeSubString(0, 12),
173 txCostUsd: txCostUsd.toString().safeSubString(0, 4),
174 overshotWei: overshotWei.toString(),
175 overshotEth: overshotEth.toString().safeSubString(0, 12),
176 overshotUsd: overshotUsd.toString().safeSubString(0, 4),
177 );
178 }
179
180 static EVMTransactionErrorFeesHandler genericInsufficientFunds() {
181 return EVMTransactionErrorFeesHandler(
182 balanceWei: '0',
183 balanceEth: '0',
184 balanceUsd: '0',
185 txCostWei: '0',
186 txCostEth: '0',
187 txCostUsd: '0',
188 overshotWei: '0',
189 overshotEth: '0',
190 overshotUsd: '0',
191 error: 'generic_insufficient_funds',
192 );
193 }
194 }
195
196 /// Represents an error parsing pattern
197 class ErrorPattern {
198 final String name;
199 final RegExp regex;
200 final Map<String, String> Function(Match) extractor;
201 final Map<String, String> Function(Map<String, String>) calculator;
202
203 ErrorPattern({
204 required this.name,
205 required this.regex,
206 required this.extractor,
207 required this.calculator,
208 });
209 }