From b46300b9963456514068d2e5502789b341c80451 Mon Sep 17 00:00:00 2001 From: dev2 Date: Thu, 5 Dec 2024 18:09:12 +0100 Subject: [PATCH 1/5] Initial --- .../repositories/rpc/evm_rpc_interface.dart | 11 ++-- lib/src/domain/entities/fee.dart | 55 ++++++++++++------- lib/src/domain/entities/tx_gasFee_entity.dart | 21 ++++++- 3 files changed, 59 insertions(+), 28 deletions(-) diff --git a/lib/src/crypto/evm/repositories/rpc/evm_rpc_interface.dart b/lib/src/crypto/evm/repositories/rpc/evm_rpc_interface.dart index 5f8d81d94..48ec7f750 100644 --- a/lib/src/crypto/evm/repositories/rpc/evm_rpc_interface.dart +++ b/lib/src/crypto/evm/repositories/rpc/evm_rpc_interface.dart @@ -216,10 +216,7 @@ final class EvmRpcInterface { List? accessList, }) async { final (gasPrice, gasLimit) = switch ((feeInfo.gasPrice, feeInfo.gasLimit)) { - (null, int gasLimit) => ( - Amount(value: await getGasPrice(), decimals: 18), - gasLimit - ), + (Amount gasPrice, int gasLimit) => (gasPrice, gasLimit), (Amount gasPrice, null) => ( gasPrice, await estimateGasLimit( @@ -229,7 +226,11 @@ final class EvmRpcInterface { value: value, ) ), - _ => await estimateNetworkFees( + (null, int gasLimit) => ( + Amount(value: await getGasPrice(), decimals: 18), + gasLimit + ), + (null, null) => await estimateNetworkFees( recipient: recipient, sender: sender, data: data, value: value), }; diff --git a/lib/src/domain/entities/fee.dart b/lib/src/domain/entities/fee.dart index 92ead7ddd..6266662e7 100644 --- a/lib/src/domain/entities/fee.dart +++ b/lib/src/domain/entities/fee.dart @@ -45,10 +45,20 @@ sealed class FeeInformation { 'gasLimit': int gasLimit, 'gasPrice': Map gasPrice, } => - EvmFeeInformation( + EvmLegacyFeeInformation( gasLimit: gasLimit, gasPrice: Amount.fromJson(gasPrice), ), + { + 'gasLimit': int gasLimit, + 'maxFeePerGas': Map maxFeePerGas, + 'maxPriorityFeePerGas': Map maxPriorityFeePerGas, + } => + EvmType2FeeInformation( + gasLimit: gasLimit, + maxFeePerGas: Amount.fromJson(maxFeePerGas), + maxPriorityFeePerGas: Amount.fromJson(maxPriorityFeePerGas), + ), { 'feePerByte': Map feePerByte, 'fee': Map? fee, @@ -68,14 +78,18 @@ sealed class FeeInformation { } } -final class EvmFeeInformation extends FeeInformation { +sealed class EvmFeeInformation extends FeeInformation { final int? gasLimit; - final Amount? gasPrice; + + Amount get fee; const EvmFeeInformation({ required this.gasLimit, - required this.gasPrice, }); +} + +final class EvmLegacyFeeInformation extends EvmFeeInformation { + final Amount? gasPrice; Amount get fee { return gasPrice ?? @@ -89,54 +103,53 @@ final class EvmFeeInformation extends FeeInformation { }; } - EvmFeeInformation copyWith({ + EvmLegacyFeeInformation copyWith({ int? gasLimit, Amount? gasPrice, }) { - return EvmFeeInformation( + return EvmLegacyFeeInformation( gasLimit: gasLimit ?? this.gasLimit, gasPrice: gasPrice ?? this.gasPrice, ); } - static EvmFeeInformation get zero { - return EvmFeeInformation(gasLimit: null, gasPrice: null); - } + const EvmLegacyFeeInformation({ + required super.gasLimit, + required this.gasPrice, + }); } final class EvmType2FeeInformation extends EvmFeeInformation { + final Amount? maxFeePerGas; final Amount? maxPriorityFeePerGas; + Amount get fee { + return maxFeePerGas ?? + Amount.zero * Amount.convert(value: gasLimit ?? 0, decimals: 0); + } + const EvmType2FeeInformation({ required super.gasLimit, - required super.gasPrice, + required this.maxFeePerGas, required this.maxPriorityFeePerGas, }); - static EvmType2FeeInformation get zero { - return EvmType2FeeInformation( - gasLimit: null, - gasPrice: null, - maxPriorityFeePerGas: null, - ); - } - Json toJson() { return { 'gasLimit': gasLimit, 'maxPriorityFeePerGas': maxPriorityFeePerGas.toString(), - 'gasPrice': gasPrice?.toJson() ?? Amount.zero.toJson(), + 'maxFeePerGas': maxFeePerGas.toString(), }; } EvmType2FeeInformation copyWith({ int? gasLimit, - Amount? gasPrice, + Amount? maxFeePerGas, Amount? maxPriorityFeePerGas, }) { return EvmType2FeeInformation( gasLimit: gasLimit ?? this.gasLimit, - gasPrice: gasPrice ?? this.gasPrice, + maxFeePerGas: maxFeePerGas ?? this.maxFeePerGas, maxPriorityFeePerGas: maxPriorityFeePerGas ?? this.maxPriorityFeePerGas, ); } diff --git a/lib/src/domain/entities/tx_gasFee_entity.dart b/lib/src/domain/entities/tx_gasFee_entity.dart index 03a3f124e..e6b6f358e 100644 --- a/lib/src/domain/entities/tx_gasFee_entity.dart +++ b/lib/src/domain/entities/tx_gasFee_entity.dart @@ -37,6 +37,10 @@ final class EvmNetworkFees extends NetworkFees { final BigInt average; final BigInt fast; + BigInt get safePriority => safe - lastBlock; + BigInt get averagePriority => average - lastBlock; + BigInt get fastPriority => fast - lastBlock; + const EvmNetworkFees({ required this.lastBlock, required this.safe, @@ -51,11 +55,23 @@ final class EvmNetworkFees extends NetworkFees { _ => safe, }; + BigInt getPriorityGWEI(FeePriority feePriority) => switch (feePriority) { + FeePriority.high => fastPriority, + FeePriority.medium => averagePriority, + FeePriority.low => safePriority, + _ => safePriority, + }; + Amount getFeeAmount(FeePriority feePriority) => Amount( value: getFeeGWEI(feePriority), decimals: 18, // GWEI ); + Amount getPriorityAmount(FeePriority feePriority) => Amount( + value: getPriorityGWEI(feePriority), + decimals: 18, // GWEI + ); + const EvmNetworkFees.fromBigInt({ required BigInt lastBlock, }) : this( @@ -68,7 +84,7 @@ final class EvmNetworkFees extends NetworkFees { factory EvmNetworkFees.fromJson(Map json) { if (json case { - // 'suggestBaseFee': String last, + 'suggestBaseFee': String last, 'SafeGasPrice': String safe, 'ProposeGasPrice': String propose, 'FastGasPrice': String fast, @@ -76,9 +92,10 @@ final class EvmNetworkFees extends NetworkFees { final safe_num = toGwei(safe); final propose_num = toGwei(propose); final fast_num = toGwei(fast); + final last_num = toGwei(last); return EvmNetworkFees( - lastBlock: safe_num, + lastBlock: last_num, safe: safe_num, average: propose_num, fast: fast_num, From 05f7ed4ebda8873adf1fef61ded5c97494354d1b Mon Sep 17 00:00:00 2001 From: dev2 Date: Mon, 9 Dec 2024 18:11:36 +0100 Subject: [PATCH 2/5] refactor: rename fee to maxFee --- lib/src/domain/entities/fee.dart | 27 +++++++++++++++----- lib/src/domain/entities/transfer_intent.dart | 4 +-- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/lib/src/domain/entities/fee.dart b/lib/src/domain/entities/fee.dart index 6266662e7..a64481a67 100644 --- a/lib/src/domain/entities/fee.dart +++ b/lib/src/domain/entities/fee.dart @@ -81,7 +81,7 @@ sealed class FeeInformation { sealed class EvmFeeInformation extends FeeInformation { final int? gasLimit; - Amount get fee; + Amount? get maxFee; const EvmFeeInformation({ required this.gasLimit, @@ -91,9 +91,15 @@ sealed class EvmFeeInformation extends FeeInformation { final class EvmLegacyFeeInformation extends EvmFeeInformation { final Amount? gasPrice; - Amount get fee { - return gasPrice ?? - Amount.zero * Amount.convert(value: gasLimit ?? 0, decimals: 0); + Amount? get maxFee { + if (gasPrice == null) { + return null; + } + if (gasLimit == null) { + return null; + } + + return gasPrice! * Amount.convert(value: gasLimit!, decimals: 0); } Json toJson() { @@ -123,9 +129,16 @@ final class EvmType2FeeInformation extends EvmFeeInformation { final Amount? maxFeePerGas; final Amount? maxPriorityFeePerGas; - Amount get fee { - return maxFeePerGas ?? - Amount.zero * Amount.convert(value: gasLimit ?? 0, decimals: 0); + Amount? get maxFee { + if (gasLimit == null) { + return null; + } + + if (maxFeePerGas == null) { + return null; + } + + return maxFeePerGas! * Amount.convert(value: gasLimit!, decimals: 0); } const EvmType2FeeInformation({ diff --git a/lib/src/domain/entities/transfer_intent.dart b/lib/src/domain/entities/transfer_intent.dart index 95780533e..ee74864a4 100644 --- a/lib/src/domain/entities/transfer_intent.dart +++ b/lib/src/domain/entities/transfer_intent.dart @@ -27,7 +27,7 @@ class TransferIntent { Amount? get fee { if (feeInfo is EvmFeeInformation) { - return (feeInfo as EvmFeeInformation).fee; + return (feeInfo as EvmFeeInformation).maxFee; } if (feeInfo is TronFeeInformation) { return (feeInfo as TronFeeInformation).feeLimit; @@ -69,7 +69,7 @@ class TransferIntent { }) { final newTargetValue = switch ((balance, feeInfo)) { (Amount balance, EvmFeeInformation info) when token.isERC20 == false => - _calcTargetAmount(balance, info.fee), + _calcTargetAmount(balance, info.maxFee), (Amount balance, TronFeeInformation info) when token.isERC20 == false => _calcTargetAmount(balance, info.feeLimit), _ => amount, From 6b1ab42a51bb00272f05a52fcee96d65bdcb7bcc Mon Sep 17 00:00:00 2001 From: "thomas.fercher" Date: Tue, 10 Dec 2024 17:59:50 +0100 Subject: [PATCH 3/5] Refactor EvmFee --- .../abi/uniswap_v2/uniswap_v2_router.dart | 24 ++-- .../entities/contract/internal_contract.dart | 4 +- .../evm/repositories/rpc/evm_client.dart | 11 ++ .../repositories/rpc/evm_rpc_interface.dart | 131 +++++++++++------- lib/src/crypto/network_type.dart | 3 + lib/src/crypto/utxo/utils/send.dart | 2 +- lib/src/domain/entities/fee.dart | 99 ++++++------- lib/src/domain/entities/transfer_intent.dart | 19 +-- test/no_ci/arb_test.dart | 1 - test/no_ci/ava_test.dart | 3 +- test/no_ci/base_test.dart | 1 - test/no_ci/contract_test.dart | 1 - test/no_ci/moon_test.dart | 2 - test/no_ci/op_test.dart | 2 - test/no_ci/sign_evmtx_test.dart | 11 -- test/no_ci/wallet_test.dart | 33 ++--- test/no_ci/zkSync_test.dart | 2 - 17 files changed, 189 insertions(+), 160 deletions(-) diff --git a/lib/src/crypto/evm/entities/abi/uniswap_v2/uniswap_v2_router.dart b/lib/src/crypto/evm/entities/abi/uniswap_v2/uniswap_v2_router.dart index 8c80342ab..33ee945d8 100644 --- a/lib/src/crypto/evm/entities/abi/uniswap_v2/uniswap_v2_router.dart +++ b/lib/src/crypto/evm/entities/abi/uniswap_v2/uniswap_v2_router.dart @@ -1291,19 +1291,17 @@ class UniswapV2Router extends InternalContract { final function = abi.getFunction("addLiquidity")!; final result = await buildTransactionForFunction( sender: sender, - function: function.addValues(values: [ - tokenA, - tokenB, - amountADesired, - amountBDesired, - amountAMin, - amountBMin, - to, - deadline, - ]), - feeInfo: EvmFeeInformation( - gasLimit: 3E5.toInt(), - gasPrice: Amount(value: 1E10.toInt().toBigInt, decimals: 18), + function: function.addValues( + values: [ + tokenA, + tokenB, + amountADesired, + amountBDesired, + amountAMin, + amountBMin, + to, + deadline, + ], ), ); return result; diff --git a/lib/src/crypto/evm/entities/contract/internal_contract.dart b/lib/src/crypto/evm/entities/contract/internal_contract.dart index 455a5c063..dd5358c8f 100644 --- a/lib/src/crypto/evm/entities/contract/internal_contract.dart +++ b/lib/src/crypto/evm/entities/contract/internal_contract.dart @@ -39,7 +39,7 @@ abstract class InternalContract { sender: sender, recipient: contractAddress, seed: seed, - feeInfo: feeInfo ?? EvmFeeInformation.zero, + feeInfo: feeInfo, data: functionData, value: value ?? BigInt.zero, accessList: accessList, @@ -69,7 +69,7 @@ abstract class InternalContract { return await rpc.buildUnsignedTransaction( sender: sender, recipient: contractAddress, - feeInfo: feeInfo ?? EvmFeeInformation.zero, + feeInfo: feeInfo, data: functionData, value: value ?? BigInt.zero, ); diff --git a/lib/src/crypto/evm/repositories/rpc/evm_client.dart b/lib/src/crypto/evm/repositories/rpc/evm_client.dart index 1f2c6e6cc..4d3a12170 100644 --- a/lib/src/crypto/evm/repositories/rpc/evm_client.dart +++ b/lib/src/crypto/evm/repositories/rpc/evm_client.dart @@ -284,6 +284,17 @@ base class EvmRpcClient { return gasPrice; } + Future getPriorityFee() async { + // Direct RPC call to get suggested priority fee + final response = await _call( + 'eth_maxPriorityFeePerGas', + ); + + final priorityFee = response.toBigIntOrNull; + if (priorityFee == null) throw Exception('Could not parse priority fee'); + return priorityFee; + } + /// /// Estimate Gas Fee /// diff --git a/lib/src/crypto/evm/repositories/rpc/evm_rpc_interface.dart b/lib/src/crypto/evm/repositories/rpc/evm_rpc_interface.dart index 48ec7f750..f3f66ea51 100644 --- a/lib/src/crypto/evm/repositories/rpc/evm_rpc_interface.dart +++ b/lib/src/crypto/evm/repositories/rpc/evm_rpc_interface.dart @@ -1,11 +1,14 @@ import 'dart:async'; import 'dart:typed_data'; +import 'package:walletkit_dart/src/common/logger.dart'; import 'package:walletkit_dart/src/crypto/evm/entities/block_number.dart'; import 'package:walletkit_dart/src/crypto/evm/repositories/rpc/queued_rpc_interface.dart'; import 'package:walletkit_dart/src/domain/exceptions.dart'; import 'package:walletkit_dart/src/utils/int.dart'; import 'package:walletkit_dart/walletkit_dart.dart'; +const type2Multiplier = 1.5; + final class EvmRpcInterface { final EVMNetworkType type; final Map blockTimestampCache = {}; @@ -126,6 +129,13 @@ final class EvmRpcInterface { ); } + /// + /// Get Gas Price + /// + Future getGasPriceAmount() => getGasPrice().then( + (value) => Amount(value: value, decimals: 18), + ); + /// /// Get Transaction Count (Nonce) /// @@ -201,6 +211,61 @@ final class EvmRpcInterface { ); } + Future getPriorityFee() async { + final priorityFee = await performTask( + (client) => client.getPriorityFee(), + ); + + return Amount(value: priorityFee, decimals: 9); + } + + Future getType2GasPrice() async { + final maxFeePerGas = await getGasPriceAmount(); + final maxPriorityFeePerGas = await getPriorityFee(); + + return EvmType2GasPrice( + maxFeePerGas: + maxFeePerGas.multiplyAndCeil(type2Multiplier) + maxPriorityFeePerGas, + maxPriorityFeePerGas: maxPriorityFeePerGas, + ); + } + + Future<(int gasLimit, EvmGasPrice gasPrice)> fetchNetworkFees({ + EvmFeeInformation? existing, + required String recipient, + required String sender, + required Uint8List? data, + required BigInt? value, + }) async { + var gasLimit = existing?.gasLimit; + try { + gasLimit ??= await estimateGasLimit( + recipient: recipient, + sender: sender, + data: data, + value: value, + ); + } catch (e) { + Logger.logError(e, hint: "Gas estimation failed"); + + // Only Debug + assert(true, "Gas estimation failed"); + + gasLimit = 1E6.toInt(); + } + + final EvmGasPrice gasPrice = switch (existing?.gasPrice) { + EvmLegacyGasPrice feeInfo => feeInfo, + EvmType2GasPrice feeInfo => feeInfo, + null when type.useEIP1559 => await getType2GasPrice(), + null => EvmLegacyGasPrice( + gasPrice: await getGasPriceAmount(), + ), + }; + + return (gasLimit, gasPrice); + } + /// /// Used to create a raw Transactions /// Fetches the gasPrice and gasLimit from the network @@ -210,56 +275,28 @@ final class EvmRpcInterface { Future buildUnsignedTransaction({ required String sender, required String recipient, - required EvmFeeInformation feeInfo, + required EvmFeeInformation? feeInfo, required Uint8List? data, required BigInt? value, List? accessList, }) async { - final (gasPrice, gasLimit) = switch ((feeInfo.gasPrice, feeInfo.gasLimit)) { - (Amount gasPrice, int gasLimit) => (gasPrice, gasLimit), - (Amount gasPrice, null) => ( - gasPrice, - await estimateGasLimit( - recipient: recipient, - sender: sender, - data: data, - value: value, - ) - ), - (null, int gasLimit) => ( - Amount(value: await getGasPrice(), decimals: 18), - gasLimit - ), - (null, null) => await estimateNetworkFees( - recipient: recipient, sender: sender, data: data, value: value), - }; + final (gasLimit, gasPrice) = await fetchNetworkFees( + recipient: recipient, + sender: sender, + data: data, + value: value, + existing: feeInfo, + ); final nonce = await performTask( (client) => client.getTransactionCount(sender), ); - if (type is ZENIQ_SMART_NETWORK) { - return RawEVMTransactionType0.unsigned( - nonce: nonce, - gasPrice: gasPrice.value, - gasLimit: gasLimit.toBI, - to: recipient, - value: value ?? BigInt.zero, - data: data ?? Uint8List(0), - ); - } - - if (feeInfo is EvmType2FeeInformation && - gasPrice.value < feeInfo.maxPriorityFeePerGas!.value) { - throw Failure("Gas price is less than max priority fee"); - } - - return switch (feeInfo) { - EvmType2FeeInformation() => RawEVMTransactionType2.unsigned( + return switch (gasPrice) { + EvmType2GasPrice fee => RawEVMTransactionType2.unsigned( nonce: nonce, - maxFeePerGas: gasPrice.value, - maxPriorityFeePerGas: - feeInfo.maxPriorityFeePerGas?.value ?? Amount.zero.value, + maxFeePerGas: fee.maxFeePerGas.value, + maxPriorityFeePerGas: fee.maxPriorityFeePerGas.value, gasLimit: gasLimit.toBI, to: recipient, value: value ?? BigInt.zero, @@ -267,10 +304,10 @@ final class EvmRpcInterface { accessList: accessList ?? [], chainId: type.chainId, ), - EvmFeeInformation() => accessList != null + EvmLegacyGasPrice fee => accessList != null ? RawEVMTransactionType1.unsigned( nonce: nonce, - gasPrice: gasPrice.value, + gasPrice: fee.gasPrice.value, gasLimit: gasLimit.toBI, to: recipient, value: value ?? BigInt.zero, @@ -280,7 +317,7 @@ final class EvmRpcInterface { ) : RawEVMTransactionType0.unsigned( nonce: nonce, - gasPrice: gasPrice.value, + gasPrice: fee.gasPrice.value, gasLimit: gasLimit.toBI, to: recipient, value: value ?? BigInt.zero, @@ -299,7 +336,7 @@ final class EvmRpcInterface { required String sender, required String recipient, required Uint8List seed, - required EvmFeeInformation feeInfo, + required EvmFeeInformation? feeInfo, required Uint8List? data, required BigInt? value, List? accessList, @@ -346,7 +383,7 @@ final class EvmRpcInterface { required String sender, required String recipient, required Uint8List seed, - required EvmFeeInformation feeInfo, + required EvmFeeInformation? feeInfo, required Uint8List? data, required BigInt? value, List? accessList, @@ -410,7 +447,7 @@ final class EvmRpcInterface { sender: sender, recipient: contractAddress, seed: seed, - feeInfo: feeInfo ?? EvmFeeInformation.zero, + feeInfo: feeInfo, data: data, value: value ?? BigInt.zero, ); @@ -478,7 +515,7 @@ final class EvmRpcInterface { function: function, sender: from, seed: seed, - feeInfo: EvmFeeInformation.zero, + feeInfo: null, ); } diff --git a/lib/src/crypto/network_type.dart b/lib/src/crypto/network_type.dart index 53e5427dd..294f88abb 100644 --- a/lib/src/crypto/network_type.dart +++ b/lib/src/crypto/network_type.dart @@ -19,12 +19,14 @@ sealed class NetworkType { sealed class EVMNetworkType extends NetworkType { final int chainId; + final bool useEIP1559; const EVMNetworkType({ required super.messagePrefix, required super.coin, required super.blockTime, required this.chainId, + this.useEIP1559 = true, }); } @@ -382,6 +384,7 @@ class ZENIQ_SMART_NETWORK extends EVMNetworkType { coin: zeniqSmart, messagePrefix: "\x19Zeniq Signed Message:\n", blockTime: 3, + useEIP1559: false, ); } diff --git a/lib/src/crypto/utxo/utils/send.dart b/lib/src/crypto/utxo/utils/send.dart index c7d64f0f7..f61d9ab8f 100644 --- a/lib/src/crypto/utxo/utils/send.dart +++ b/lib/src/crypto/utxo/utils/send.dart @@ -21,7 +21,7 @@ import 'package:walletkit_dart/walletkit_dart.dart'; /// RawTransaction buildUnsignedTransaction({ - required TransferIntent intent, + required TransferIntent intent, required UTXONetworkType networkType, required HDWalletPath walletPath, required Iterable txList, diff --git a/lib/src/domain/entities/fee.dart b/lib/src/domain/entities/fee.dart index a64481a67..fa73be868 100644 --- a/lib/src/domain/entities/fee.dart +++ b/lib/src/domain/entities/fee.dart @@ -45,19 +45,21 @@ sealed class FeeInformation { 'gasLimit': int gasLimit, 'gasPrice': Map gasPrice, } => - EvmLegacyFeeInformation( + EvmFeeInformation( gasLimit: gasLimit, - gasPrice: Amount.fromJson(gasPrice), + gasPrice: EvmLegacyGasPrice(gasPrice: Amount.fromJson(gasPrice)), ), { 'gasLimit': int gasLimit, 'maxFeePerGas': Map maxFeePerGas, 'maxPriorityFeePerGas': Map maxPriorityFeePerGas, } => - EvmType2FeeInformation( + EvmFeeInformation( gasLimit: gasLimit, - maxFeePerGas: Amount.fromJson(maxFeePerGas), - maxPriorityFeePerGas: Amount.fromJson(maxPriorityFeePerGas), + gasPrice: EvmType2GasPrice( + maxFeePerGas: Amount.fromJson(maxFeePerGas), + maxPriorityFeePerGas: Amount.fromJson(maxPriorityFeePerGas), + ), ), { 'feePerByte': Map feePerByte, @@ -78,90 +80,93 @@ sealed class FeeInformation { } } -sealed class EvmFeeInformation extends FeeInformation { +final class EvmFeeInformation extends FeeInformation { final int? gasLimit; - - Amount? get maxFee; + final EvmGasPrice? gasPrice; const EvmFeeInformation({ - required this.gasLimit, + this.gasLimit, + this.gasPrice, }); -} - -final class EvmLegacyFeeInformation extends EvmFeeInformation { - final Amount? gasPrice; Amount? get maxFee { - if (gasPrice == null) { + if (gasLimit == null) { return null; } - if (gasLimit == null) { + if (gasPrice == null) { return null; } - return gasPrice! * Amount.convert(value: gasLimit!, decimals: 0); - } - - Json toJson() { - return { - 'gasLimit': gasLimit, - 'gasPrice': gasPrice?.toJson() ?? Amount.zero.toJson(), + return switch (gasPrice!) { + EvmLegacyGasPrice gasPrice => gasPrice.gasPrice * + Amount.convert( + value: gasLimit!, + decimals: 0, + ), + EvmType2GasPrice gasPrice => + gasPrice.maxFeePerGas * Amount.convert(value: gasLimit!, decimals: 0), }; } - EvmLegacyFeeInformation copyWith({ + EvmFeeInformation copyWith({ int? gasLimit, - Amount? gasPrice, + EvmGasPrice? gasPrice, }) { - return EvmLegacyFeeInformation( + return EvmFeeInformation( gasLimit: gasLimit ?? this.gasLimit, gasPrice: gasPrice ?? this.gasPrice, ); } - const EvmLegacyFeeInformation({ - required super.gasLimit, - required this.gasPrice, - }); + Json toJson() { + return { + 'gasLimit': gasLimit, + ...?gasPrice?.toJson(), + }; + } } -final class EvmType2FeeInformation extends EvmFeeInformation { - final Amount? maxFeePerGas; - final Amount? maxPriorityFeePerGas; +sealed class EvmGasPrice { + const EvmGasPrice(); - Amount? get maxFee { - if (gasLimit == null) { - return null; - } + Json toJson(); +} - if (maxFeePerGas == null) { - return null; - } +final class EvmLegacyGasPrice extends EvmGasPrice { + final Amount gasPrice; - return maxFeePerGas! * Amount.convert(value: gasLimit!, decimals: 0); + const EvmLegacyGasPrice({ + required this.gasPrice, + }); + + Json toJson() { + return { + 'gasPrice': gasPrice.toJson(), + }; } +} - const EvmType2FeeInformation({ - required super.gasLimit, +final class EvmType2GasPrice extends EvmGasPrice { + final Amount maxFeePerGas; + final Amount maxPriorityFeePerGas; + + const EvmType2GasPrice({ required this.maxFeePerGas, required this.maxPriorityFeePerGas, }); Json toJson() { return { - 'gasLimit': gasLimit, 'maxPriorityFeePerGas': maxPriorityFeePerGas.toString(), 'maxFeePerGas': maxFeePerGas.toString(), }; } - EvmType2FeeInformation copyWith({ - int? gasLimit, + EvmType2GasPrice copyWith({ Amount? maxFeePerGas, Amount? maxPriorityFeePerGas, }) { - return EvmType2FeeInformation( - gasLimit: gasLimit ?? this.gasLimit, + return EvmType2GasPrice( maxFeePerGas: maxFeePerGas ?? this.maxFeePerGas, maxPriorityFeePerGas: maxPriorityFeePerGas ?? this.maxPriorityFeePerGas, ); diff --git a/lib/src/domain/entities/transfer_intent.dart b/lib/src/domain/entities/transfer_intent.dart index ee74864a4..ff57e5742 100644 --- a/lib/src/domain/entities/transfer_intent.dart +++ b/lib/src/domain/entities/transfer_intent.dart @@ -2,29 +2,30 @@ import 'dart:convert'; import 'dart:typed_data'; import 'package:walletkit_dart/walletkit_dart.dart'; -class TransferIntent { +class TransferIntent { final CoinEntity token; final String recipient; final Amount amount; /// If null, the fee will be calculated by the network - final T feeInfo; + final T? feeInfo; + /// For EVM represents UTF8 Data in the input field + /// For Tron and UTXO not implemented final String? memo; + + /// Is only respected for EVM Transactions final List? accessList; const TransferIntent({ required this.recipient, required this.amount, - required this.feeInfo, required this.token, required this.memo, this.accessList, + this.feeInfo, }); - bool get isType2 => feeInfo is EvmType2FeeInformation; - bool get isType1 => accessList != null; - Amount? get fee { if (feeInfo is EvmFeeInformation) { return (feeInfo as EvmFeeInformation).maxFee; @@ -68,8 +69,9 @@ class TransferIntent { Amount? balance, }) { final newTargetValue = switch ((balance, feeInfo)) { - (Amount balance, EvmFeeInformation info) when token.isERC20 == false => - _calcTargetAmount(balance, info.maxFee), + (Amount balance, EvmFeeInformation info) + when token.isERC20 == false && info.maxFee != null => + _calcTargetAmount(balance, info.maxFee!), (Amount balance, TronFeeInformation info) when token.isERC20 == false => _calcTargetAmount(balance, info.feeLimit), _ => amount, @@ -81,6 +83,7 @@ class TransferIntent { feeInfo: feeInfo, token: token, memo: memo, + accessList: accessList, ); } diff --git a/test/no_ci/arb_test.dart b/test/no_ci/arb_test.dart index 739b50ed5..a48d0cef9 100644 --- a/test/no_ci/arb_test.dart +++ b/test/no_ci/arb_test.dart @@ -27,7 +27,6 @@ void main() { final intent = TransferIntent( recipient: arbitrumTestWallet, amount: Amount.convert(value: 0.001, decimals: 18), - feeInfo: EvmFeeInformation.zero, token: ethArbitrum, memo: null, ); diff --git a/test/no_ci/ava_test.dart b/test/no_ci/ava_test.dart index f8c7a2ffc..82b3b2ee4 100644 --- a/test/no_ci/ava_test.dart +++ b/test/no_ci/ava_test.dart @@ -8,7 +8,7 @@ void main() { final intent = TransferIntent( recipient: arbitrumTestWallet, amount: Amount.convert(value: 0.001, decimals: 18), - feeInfo: EvmFeeInformation.zero, + feeInfo: null, token: avalanche, memo: null, ); @@ -27,7 +27,6 @@ void main() { final intent = TransferIntent( recipient: arbitrumTestWallet, amount: Amount.convert(value: 0.0005, decimals: 18), - feeInfo: EvmFeeInformation.zero, token: wrappedETH, memo: null, ); diff --git a/test/no_ci/base_test.dart b/test/no_ci/base_test.dart index 0e79e0918..48bdd904f 100644 --- a/test/no_ci/base_test.dart +++ b/test/no_ci/base_test.dart @@ -8,7 +8,6 @@ void main() { final intent = TransferIntent( recipient: arbitrumTestWallet, amount: Amount.convert(value: 0.001, decimals: 18), - feeInfo: EvmFeeInformation.zero, token: ethBase, memo: null, ); diff --git a/test/no_ci/contract_test.dart b/test/no_ci/contract_test.dart index 01fa08b6f..1cfc00462 100644 --- a/test/no_ci/contract_test.dart +++ b/test/no_ci/contract_test.dart @@ -246,7 +246,6 @@ void main() { final intent = TransferIntent( recipient: arbitrumTestWallet, amount: Amount.convert(value: 10, decimals: 18), - feeInfo: EvmFeeInformation.zero, token: arbitrum, memo: null, ); diff --git a/test/no_ci/moon_test.dart b/test/no_ci/moon_test.dart index f1a545678..cad1be4a0 100644 --- a/test/no_ci/moon_test.dart +++ b/test/no_ci/moon_test.dart @@ -8,7 +8,6 @@ void main() { final intent = TransferIntent( recipient: arbitrumTestWallet, amount: Amount.convert(value: 0.001, decimals: 18), - feeInfo: EvmFeeInformation.zero, token: moonbeam, memo: null, ); @@ -27,7 +26,6 @@ void main() { final intent = TransferIntent( recipient: arbitrumTestWallet, amount: Amount.convert(value: 0.01, decimals: 18), - feeInfo: EvmFeeInformation.zero, token: frax, memo: null, ); diff --git a/test/no_ci/op_test.dart b/test/no_ci/op_test.dart index a1b22175a..a39122913 100644 --- a/test/no_ci/op_test.dart +++ b/test/no_ci/op_test.dart @@ -9,7 +9,6 @@ void main() { final intent = TransferIntent( recipient: arbitrumTestWallet, amount: Amount.convert(value: 0.001, decimals: 18), - feeInfo: EvmFeeInformation.zero, token: optimism, memo: null, ); @@ -27,7 +26,6 @@ void main() { final intentETH = TransferIntent( recipient: arbitrumTestWallet, amount: Amount.convert(value: 0.001, decimals: 18), - feeInfo: EvmFeeInformation.zero, token: ethOptimism, memo: null, ); diff --git a/test/no_ci/sign_evmtx_test.dart b/test/no_ci/sign_evmtx_test.dart index 91d60729b..8c4783283 100644 --- a/test/no_ci/sign_evmtx_test.dart +++ b/test/no_ci/sign_evmtx_test.dart @@ -118,7 +118,6 @@ void main() { final intent = TransferIntent( recipient: arbitrumTestWallet, amount: Amount.convert(value: 0.001, decimals: 18), - feeInfo: EvmType2FeeInformation.zero, token: arbitrum, memo: null, ); @@ -135,12 +134,6 @@ void main() { final intent = TransferIntent( recipient: arbitrumTestWallet, amount: Amount.convert(value: 0.001, decimals: 18), - // feeInfo: EvmType2FeeInformation.zero, - feeInfo: EvmType2FeeInformation( - gasLimit: null, - gasPrice: null, - maxPriorityFeePerGas: Amount.convert(value: 0.0001, decimals: 9), - ), token: ethArbitrum, memo: null, ); @@ -157,7 +150,6 @@ void main() { final intent = TransferIntent( recipient: arbitrumTestWallet, amount: Amount.convert(value: 0.001, decimals: 18), - feeInfo: EvmType2FeeInformation.zero, token: zeniqSmart, memo: null, ); @@ -174,10 +166,8 @@ void main() { final intent = TransferIntent( recipient: arbitrumTestWallet, amount: Amount.convert(value: 0.001, decimals: 18), - feeInfo: EvmFeeInformation.zero, token: ethArbitrum, memo: null, - accessList: [], ); final hash = await arbitrumRPC.sendCoin( @@ -193,7 +183,6 @@ void main() { final intent = TransferIntent( recipient: arbitrumTestWallet, amount: Amount.convert(value: 0.001, decimals: 18), - feeInfo: EvmFeeInformation.zero, token: ethArbitrum, memo: "Hello my friend", ); diff --git a/test/no_ci/wallet_test.dart b/test/no_ci/wallet_test.dart index b20ac437f..3706259ff 100644 --- a/test/no_ci/wallet_test.dart +++ b/test/no_ci/wallet_test.dart @@ -18,7 +18,6 @@ void main() { intent: TransferIntent( recipient: arbitrumTestWallet, amount: Amount.convert(value: 0.1, decimals: zeniqSmart.decimals), - feeInfo: EvmFeeInformation.zero, token: zeniqSmart, memo: null, ), @@ -34,7 +33,6 @@ void main() { intent: TransferIntent( recipient: arbitrumTestWallet, amount: Amount.convert(value: 0.001, decimals: avinocZSC.decimals), - feeInfo: EvmFeeInformation.zero, token: avinocZSC, memo: null, ), @@ -58,16 +56,13 @@ void main() { txs: txList, ); - final intent = TransferIntent( - recipient: reveiveAddress, - amount: Amount(value: BigInt.from(100000000), decimals: 8), - feeInfo: null, - token: zeniqCoin, - memo: null, - ); - final unsignedTx = buildUnsignedTransaction( - intent: intent, + intent: TransferIntent( + recipient: reveiveAddress, + amount: Amount(value: BigInt.from(100000000), decimals: 8), + token: zeniqCoin, + memo: null, + ), networkType: ZeniqNetwork, walletPath: bitcoinNSHDPath, txList: txList, @@ -122,16 +117,14 @@ void main() { print("Receive Address: $receive"); - final intent = TransferIntent( - recipient: receive, - amount: Amount.convert(value: 1.2, decimals: 5), - feeInfo: null, - token: ec8Coin, - memo: null, - ); - final unsignedTx = buildUnsignedTransaction( - intent: intent, + intent: TransferIntent( + recipient: receive, + amount: Amount.convert(value: 1.2, decimals: 5), + feeInfo: null, + token: ec8Coin, + memo: null, + ), networkType: EurocoinNetwork, walletPath: bitcoinNSHDPath, txList: txList, diff --git a/test/no_ci/zkSync_test.dart b/test/no_ci/zkSync_test.dart index 5aa513489..c0c192c3f 100644 --- a/test/no_ci/zkSync_test.dart +++ b/test/no_ci/zkSync_test.dart @@ -9,7 +9,6 @@ void main() { final intent = TransferIntent( recipient: arbitrumTestWallet, amount: Amount.convert(value: 0.00004, decimals: 8), - feeInfo: EvmFeeInformation.zero, token: wbtcZKSync, memo: null, ); @@ -27,7 +26,6 @@ void main() { final intentETH = TransferIntent( recipient: arbitrumTestWallet, amount: Amount.convert(value: 0.001, decimals: 18), - feeInfo: EvmFeeInformation.zero, token: ethzkSync, memo: null, ); From d5085560e9584a81fa7bf53faf0fc9ee92e4c9de Mon Sep 17 00:00:00 2001 From: "thomas.fercher" Date: Tue, 10 Dec 2024 18:56:56 +0100 Subject: [PATCH 4/5] Include Priority in EvmNetworkFees --- lib/src/crypto/network_type.dart | 1 + lib/src/domain/entities/tx_gasFee_entity.dart | 42 +++++++++++++++++-- 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/lib/src/crypto/network_type.dart b/lib/src/crypto/network_type.dart index 294f88abb..cf7606303 100644 --- a/lib/src/crypto/network_type.dart +++ b/lib/src/crypto/network_type.dart @@ -397,6 +397,7 @@ class BNB_NETWORK extends EVMNetworkType { coin: binanceSmart, messagePrefix: "\x19Binance Chain Signed Message:\n", blockTime: 3, + useEIP1559: true, ); } diff --git a/lib/src/domain/entities/tx_gasFee_entity.dart b/lib/src/domain/entities/tx_gasFee_entity.dart index e6b6f358e..abeb05c06 100644 --- a/lib/src/domain/entities/tx_gasFee_entity.dart +++ b/lib/src/domain/entities/tx_gasFee_entity.dart @@ -37,15 +37,18 @@ final class EvmNetworkFees extends NetworkFees { final BigInt average; final BigInt fast; - BigInt get safePriority => safe - lastBlock; - BigInt get averagePriority => average - lastBlock; - BigInt get fastPriority => fast - lastBlock; + final BigInt safePriority; + final BigInt averagePriority; + final BigInt fastPriority; const EvmNetworkFees({ required this.lastBlock, required this.safe, required this.average, required this.fast, + required this.safePriority, + required this.averagePriority, + required this.fastPriority, }); BigInt getFeeGWEI(FeePriority feePriority) => switch (feePriority) { @@ -74,11 +77,15 @@ final class EvmNetworkFees extends NetworkFees { const EvmNetworkFees.fromBigInt({ required BigInt lastBlock, + required BigInt maxPriorityFeePerGas, }) : this( lastBlock: lastBlock, safe: lastBlock, average: lastBlock, fast: lastBlock, + safePriority: maxPriorityFeePerGas, + averagePriority: maxPriorityFeePerGas, + fastPriority: maxPriorityFeePerGas, ); factory EvmNetworkFees.fromJson(Map json) { @@ -94,11 +101,40 @@ final class EvmNetworkFees extends NetworkFees { final fast_num = toGwei(fast); final last_num = toGwei(last); + final safePriority = safe_num - last_num; + final averagePriority = propose_num - last_num; + final fastPriority = fast_num - last_num; + return EvmNetworkFees( lastBlock: last_num, safe: safe_num, average: propose_num, fast: fast_num, + safePriority: safePriority, + averagePriority: averagePriority, + fastPriority: fastPriority, + ); + } + + if (json + case { + 'SafeGasPrice': String safe, + 'ProposeGasPrice': String average, + 'FastGasPrice': String fast, + }) { + final safe_num = toGwei(safe); + final last_num = safe_num; + final average_num = toGwei(average); + final fast_num = toGwei(fast); + + return EvmNetworkFees( + lastBlock: last_num, + safe: safe_num, + average: average_num, + fast: fast_num, + safePriority: BigInt.zero, + averagePriority: BigInt.zero, + fastPriority: BigInt.zero, ); } From cbaa9a5c63434973e3b163700aeb810d5a2a4cc9 Mon Sep 17 00:00:00 2001 From: dev2 Date: Thu, 12 Dec 2024 16:01:24 +0100 Subject: [PATCH 5/5] refactor: make EvmFeeInformation generic for gasPrice type --- lib/src/domain/entities/fee.dart | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/src/domain/entities/fee.dart b/lib/src/domain/entities/fee.dart index fa73be868..f04aca146 100644 --- a/lib/src/domain/entities/fee.dart +++ b/lib/src/domain/entities/fee.dart @@ -80,9 +80,9 @@ sealed class FeeInformation { } } -final class EvmFeeInformation extends FeeInformation { +final class EvmFeeInformation extends FeeInformation { final int? gasLimit; - final EvmGasPrice? gasPrice; + final T? gasPrice; const EvmFeeInformation({ this.gasLimit, @@ -108,9 +108,9 @@ final class EvmFeeInformation extends FeeInformation { }; } - EvmFeeInformation copyWith({ + EvmFeeInformation copyWith({ int? gasLimit, - EvmGasPrice? gasPrice, + T? gasPrice, }) { return EvmFeeInformation( gasLimit: gasLimit ?? this.gasLimit,