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 }