CW-1086: Polygon Issues (#2329)
* fix(polygon): Polygon wallets not showing in address book when sending tokens * feat(polygon_issues): Enhance gas price handling in EVM Chains This change: - Updates gas price calculation to account for minimum priority fee in Polygon - Adjusts gas price handling to use maxFeePerGas when gasPrice is not provided. - Fixes issue with send all for Polygon
David Adegoke committed
Jun 24, 2025 at 03:41 UTC
4434ad7363f300f6002b442413b7f28ec14c0af8
5 files changed
+63
-16
cw_evm/lib/evm_chain_client.dart
+4
-1
@@ -109,7 +109,6 @@ abstract class EVMChainClient {
109
sender: senderAddress,
110
to: toAddress,
111
value: value,
112
- // maxFeePerGas: maxFeePerGas,
112
);
113
114
return estimatedGas.toInt();
@@ -165,6 +164,7 @@ abstract class EVMChainClient {
164
required int exponent,
165
String? contractAddress,
166
String? data,
167
+ int? gasPrice,
168
}) async {
169
assert(currency == CryptoCurrency.eth ||
170
currency == CryptoCurrency.maticpoly ||
@@ -180,6 +180,7 @@ abstract class EVMChainClient {
180
data: data != null ? hexToBytes(data) : null,
181
maxGas: estimatedGasUnits,
182
maxFeePerGas: EtherAmount.fromInt(EtherUnit.wei, maxFeePerGas),
183
+ gasPrice: gasPrice != null ? EtherAmount.fromInt(EtherUnit.wei, gasPrice) : null,
184
);
185
186
Uint8List signedTransaction;
@@ -224,6 +225,7 @@ abstract class EVMChainClient {
225
required EVMChainTransactionPriority priority,
226
required int exponent,
227
required String contractAddress,
228
+ int? gasPrice,
229
}) async {
230
231
final Transaction transaction = createTransaction(
@@ -233,6 +235,7 @@ abstract class EVMChainClient {
235
amount: EtherAmount.zero(),
236
maxGas: estimatedGasUnits,
237
maxFeePerGas: EtherAmount.fromInt(EtherUnit.wei, maxFeePerGas),
238
+ gasPrice: gasPrice != null ? EtherAmount.fromInt(EtherUnit.wei, gasPrice) : null,
239
);
240
241
final erc20 = ERC20(
cw_evm/lib/evm_chain_wallet.dart
+43
-11
@@ -263,31 +263,65 @@ abstract class EVMChainWalletBase
263
final priorityFee = EtherAmount.fromInt(EtherUnit.gwei, priority.tip).getInWei.toInt();
264
265
int maxFeePerGas;
266
+ int adjustedGasPrice;
267
+
268
+ bool isPolygon = _client.chainId == 137;
269
+
270
if (gasBaseFee != null) {
271
// MaxFeePerGas with EIP1559;
272
maxFeePerGas = gasBaseFee! + priorityFee;
273
} else {
274
// MaxFeePerGas with gasPrice
271
- maxFeePerGas = gasPrice;
275
+ maxFeePerGas = gasPrice + priorityFee;
276
+ }
277
+
278
+ adjustedGasPrice = maxFeePerGas;
279
+
280
+ // Polygon has a minimum priority fee of 25 gwei
281
+ if (isPolygon) {
282
+ int minPriorityFee = 25;
283
+ int minPriorityFeeWei =
284
+ EtherAmount.fromInt(EtherUnit.gwei, minPriorityFee).getInWei.toInt();
285
+
286
+ // Calculate user selected priority-based additional fee on top of minimum
287
+ int additionalPriorityFee = 0;
288
+ switch (priority) {
289
+ case EVMChainTransactionPriority.slow:
290
+ // We use minimum priority fee only
291
+ additionalPriorityFee = 0;
292
+ break;
293
+ case EVMChainTransactionPriority.medium:
294
+ // We add 15 gwei on top of minimum
295
+ additionalPriorityFee = EtherAmount.fromInt(EtherUnit.gwei, 15).getInWei.toInt();
296
+ break;
297
+ case EVMChainTransactionPriority.fast:
298
+ // We add 35 gwei on top of minimum
299
+ additionalPriorityFee = EtherAmount.fromInt(EtherUnit.gwei, 35).getInWei.toInt();
300
+ break;
301
+ }
302
+
303
+ int totalPriorityFee = minPriorityFeeWei + additionalPriorityFee;
304
+ adjustedGasPrice = gasPrice + totalPriorityFee;
305
+ maxFeePerGas = gasPrice + totalPriorityFee;
306
}
307
308
final estimatedGas = await _client.getEstimatedGasUnitsForTransaction(
309
contractAddress: contractAddress,
310
senderAddress: _evmChainPrivateKey.address,
311
value: EtherAmount.fromBigInt(EtherUnit.wei, amount!),
278
- gasPrice: EtherAmount.fromInt(EtherUnit.wei, gasPrice),
312
+ gasPrice: EtherAmount.fromInt(EtherUnit.wei, adjustedGasPrice),
313
toAddress: EthereumAddress.fromHex(receivingAddressHex),
314
maxFeePerGas: EtherAmount.fromInt(EtherUnit.wei, maxFeePerGas),
315
data: data,
316
);
317
284
- final totalGasFee = estimatedGas * maxFeePerGas;
318
+ final totalGasFee = estimatedGas * adjustedGasPrice;
319
320
return GasParamsHandler(
321
estimatedGasUnits: estimatedGas,
322
estimatedGasFee: totalGasFee,
323
maxFeePerGas: maxFeePerGas,
290
- gasPrice: gasPrice,
324
+ gasPrice: adjustedGasPrice,
325
);
326
}
327
return GasParamsHandler.zero();
@@ -476,23 +510,20 @@ abstract class EVMChainWalletBase
510
contractAddress:
511
transactionCurrency is Erc20Token ? transactionCurrency.contractAddress : null,
512
data: hexOpReturnMemo,
513
+ gasPrice: maxFeePerGasForTransaction,
514
);
515
516
return pendingEVMChainTransaction;
517
}
518
484
- Future<PendingTransaction> createApprovalTransaction(
485
- BigInt amount,
486
- String spender,
487
- CryptoCurrency token,
488
- EVMChainTransactionPriority priority) async {
519
+ Future<PendingTransaction> createApprovalTransaction(BigInt amount, String spender,
520
+ CryptoCurrency token, EVMChainTransactionPriority priority) async {
521
final CryptoCurrency transactionCurrency =
522
balance.keys.firstWhere((element) => element.title == token.title);
523
assert(transactionCurrency is Erc20Token);
524
525
final data = _client.getEncodedDataForApprovalTransaction(
494
- contractAddress: EthereumAddress.fromHex(
495
- (transactionCurrency as Erc20Token).contractAddress),
526
+ contractAddress: EthereumAddress.fromHex((transactionCurrency as Erc20Token).contractAddress),
527
value: EtherAmount.fromBigInt(EtherUnit.wei, amount),
528
toAddress: EthereumAddress.fromHex(spender),
529
);
@@ -515,6 +546,7 @@ abstract class EVMChainWalletBase
546
estimatedGasUnits: gasFeesModel.estimatedGasUnits,
547
exponent: transactionCurrency.decimal,
548
contractAddress: transactionCurrency.contractAddress,
549
+ gasPrice: gasFeesModel.gasPrice,
550
);
551
}
552
cw_polygon/lib/default_polygon_erc20_tokens.dart
+1
-1
@@ -84,6 +84,6 @@ class DefaultPolygonErc20Tokens {
84
.iconPath;
85
} catch (_) {}
86
87
- return Erc20Token.copyWith(token, iconPath, 'POLY');
87
+ return Erc20Token.copyWith(token, iconPath, 'POL');
88
}).toList();
89
}
cw_polygon/lib/polygon_client.dart
+9
-2
@@ -18,13 +18,20 @@ class PolygonClient extends EVMChainClient {
18
EtherAmount? gasPrice,
19
EtherAmount? maxFeePerGas,
20
}) {
21
+ EtherAmount? finalGasPrice = gasPrice;
22
+
23
+ if (gasPrice == null && maxFeePerGas != null) {
24
+ // If we have EIP-1559 parameters but no legacy gasPrice, then use maxFeePerGas as gasPrice
25
+ finalGasPrice = maxFeePerGas;
26
+ }
27
+
28
return Transaction(
29
from: from,
30
to: to,
31
value: amount,
25
- // data: data,
32
+ data: data,
33
maxGas: maxGas,
27
- // gasPrice: gasPrice,
34
+ gasPrice: finalGasPrice,
35
// maxFeePerGas: maxFeePerGas,
36
// maxPriorityFeePerGas: maxPriorityFeePerGas,
37
);
cw_polygon/lib/polygon_wallet.dart
+6
-1
@@ -45,7 +45,12 @@ class PolygonWallet extends EVMChainWallet {
45
final initialErc20Tokens = DefaultPolygonErc20Tokens().initialPolygonErc20Tokens;
46
47
for (final token in initialErc20Tokens) {
48
- if (!evmChainErc20TokensBox.containsKey(token.contractAddress)) {
48
+ if (evmChainErc20TokensBox.containsKey(token.contractAddress)) {
49
+ final existingToken = evmChainErc20TokensBox.get(token.contractAddress);
50
+ if (existingToken?.tag != token.tag) {
51
+ evmChainErc20TokensBox.put(token.contractAddress, token);
52
+ }
53
+ } else {
54
if (isMigration) token.enabled = false;
55
evmChainErc20TokensBox.put(token.contractAddress, token);
56
}