From bfda5456466f09019600bd5d61dd9797be88873e Mon Sep 17 00:00:00 2001 From: Anthony Laibe Date: Mon, 28 Feb 2022 13:30:36 +0100 Subject: [PATCH] refactor: add back eip1559 --- src/app/modules/main/module.nim | 2 +- .../main/wallet_section/controller.nim | 9 +- .../wallet_section/controller_interface.nim | 2 + .../main/wallet_section/io_interface.nim | 3 + .../modules/main/wallet_section/module.nim | 9 +- .../transactions/controller.nim | 10 +- .../transactions/controller_interface.nim | 9 +- .../transactions/io_interface.nim | 6 ++ .../wallet_section/transactions/module.nim | 5 + .../main/wallet_section/transactions/view.nim | 8 ++ src/app/modules/main/wallet_section/view.nim | 3 + src/app_service/service/ens/service.nim | 4 +- src/app_service/service/eth/utils.nim | 1 + src/app_service/service/network/service.nim | 13 ++- .../service/network/service_interface.nim | 3 + src/app_service/service/settings/service.nim | 17 ---- .../service/settings/service_interface.nim | 6 -- src/app_service/service/stickers/service.nim | 2 +- .../service/transaction/service.nim | 93 +++++++++++++++++-- src/backend/eth.nim | 8 ++ .../AppLayouts/Browser/stores/WalletStore.qml | 4 + .../Browser/views/WebProviderObj.qml | 2 +- ui/app/AppLayouts/stores/RootStore.qml | 12 +++ ui/imports/shared/controls/GasSelector.qml | 48 +++++----- ui/imports/shared/popups/SendModal.qml | 13 ++- .../shared/popups/SignTransactionModal.qml | 15 +-- .../status/StatusETHTransactionModal.qml | 13 ++- .../status/StatusSNTTransactionModal.qml | 11 ++- 28 files changed, 245 insertions(+), 86 deletions(-) diff --git a/src/app/modules/main/module.nim b/src/app/modules/main/module.nim index bad216e3d6..247eaf9cfc 100644 --- a/src/app/modules/main/module.nim +++ b/src/app/modules/main/module.nim @@ -136,7 +136,7 @@ proc newModule*[T]( result.walletSectionModule = wallet_section_module.newModule[Module[T]]( result, events, tokenService, transactionService, collectible_service, walletAccountService, - settingsService, savedAddressService + settingsService, savedAddressService, networkService, ) result.browserSectionModule = browser_section_module.newModule(result, bookmarkService, settingsService, dappPermissionsService, providerService) diff --git a/src/app/modules/main/wallet_section/controller.nim b/src/app/modules/main/wallet_section/controller.nim index 05a40b9dd5..14a87b3f9e 100644 --- a/src/app/modules/main/wallet_section/controller.nim +++ b/src/app/modules/main/wallet_section/controller.nim @@ -1,6 +1,7 @@ import ./controller_interface import ../../../../app_service/service/settings/service_interface as settings_service import ../../../../app_service/service/wallet_account/service as wallet_account_service +import ../../../../app_service/service/network/service as network_service export controller_interface @@ -9,16 +10,19 @@ type delegate: T settingsService: settings_service.ServiceInterface walletAccountService: wallet_account_service.ServiceInterface - + networkService: network_service.ServiceInterface + proc newController*[T]( delegate: T, settingsService: settings_service.ServiceInterface, walletAccountService: wallet_account_service.ServiceInterface, + networkService: network_service.ServiceInterface, ): Controller[T] = result = Controller[T]() result.delegate = delegate result.settingsService = settingsService result.walletAccountService = walletAccountService + result.networkService = networkService method delete*[T](self: Controller[T]) = discard @@ -40,3 +44,6 @@ method getCurrencyBalance*[T](self: Controller[T]): float64 = method updateCurrency*[T](self: Controller[T], currency: string) = self.walletAccountService.updateCurrency(currency) + +method isEIP1559Enabled*[T](self: Controller[T]): bool = + return self.networkService.isEIP1559Enabled() \ No newline at end of file diff --git a/src/app/modules/main/wallet_section/controller_interface.nim b/src/app/modules/main/wallet_section/controller_interface.nim index 47e4d7613e..f704294c04 100644 --- a/src/app/modules/main/wallet_section/controller_interface.nim +++ b/src/app/modules/main/wallet_section/controller_interface.nim @@ -23,6 +23,8 @@ method getCurrencyBalance*(self: AccessInterface): float64 {.base.} = method updateCurrency*(self: AccessInterface, currency: string) {.base.} = raise newException(ValueError, "No implementation available") +method isEIP1559Enabled*(self: AccessInterface): bool {.base.} = + raise newException(ValueError, "No implementation available") type ## Abstract class (concept) which must be implemented by object/s used in this diff --git a/src/app/modules/main/wallet_section/io_interface.nim b/src/app/modules/main/wallet_section/io_interface.nim index 0df1d3cbdf..39fd78dc46 100644 --- a/src/app/modules/main/wallet_section/io_interface.nim +++ b/src/app/modules/main/wallet_section/io_interface.nim @@ -48,6 +48,9 @@ method transactionsModuleDidLoad*(self: AccessInterface) {.base.} = method savedAddressesModuleDidLoad*(self: AccessInterface) {.base.} = raise newException(ValueError, "No implementation available") +method isEIP1559Enabled*(self: AccessInterface): bool {.base.} = + raise newException(ValueError, "No implementation available") + type ## Abstract class (concept) which must be implemented by object/s used in this ## module. diff --git a/src/app/modules/main/wallet_section/module.nim b/src/app/modules/main/wallet_section/module.nim index 626fa0bda0..63aedc638e 100644 --- a/src/app/modules/main/wallet_section/module.nim +++ b/src/app/modules/main/wallet_section/module.nim @@ -19,6 +19,7 @@ import ../../../../app_service/service/collectible/service as collectible_servic import ../../../../app_service/service/wallet_account/service as wallet_account_service import ../../../../app_service/service/settings/service_interface as settings_service import ../../../../app_service/service/saved_address/service_interface as saved_address_service +import ../../../../app_service/service/network/service_interface as network_service import io_interface export io_interface @@ -48,12 +49,13 @@ proc newModule*[T]( walletAccountService: wallet_account_service.ServiceInterface, settingsService: settings_service.ServiceInterface, savedAddressService: saved_address_service.ServiceInterface, + networkService: network_service.ServiceInterface, ): Module[T] = result = Module[T]() result.delegate = delegate result.events = events result.moduleLoaded = false - result.controller = newController(result, settingsService, walletAccountService) + result.controller = newController(result, settingsService, walletAccountService, networkService) result.view = newView(result) result.accountTokensModule = account_tokens_module.newModule(result, events, walletAccountService) @@ -87,6 +89,9 @@ method switchAccount*[T](self: Module[T], accountIndex: int) = method setTotalCurrencyBalance*[T](self: Module[T]) = self.view.setTotalCurrencyBalance(self.controller.getCurrencyBalance()) +method isEIP1559Enabled*[T](self: Module[T]): bool = + return self.controller.isEIP1559Enabled() + method load*[T](self: Module[T]) = singletonInstance.engine.setRootContextProperty("walletSection", newQVariant(self.view)) @@ -168,4 +173,4 @@ method transactionsModuleDidLoad*[T](self: Module[T]) = self.checkIfModuleDidLoad() method savedAddressesModuleDidLoad*[T](self: Module[T]) = - self.checkIfModuleDidLoad() + self.checkIfModuleDidLoad() \ No newline at end of file diff --git a/src/app/modules/main/wallet_section/transactions/controller.nim b/src/app/modules/main/wallet_section/transactions/controller.nim index d2164a440c..41159ffa77 100644 --- a/src/app/modules/main/wallet_section/transactions/controller.nim +++ b/src/app/modules/main/wallet_section/transactions/controller.nim @@ -93,6 +93,14 @@ method transferEth*(self: Controller, from_addr: string, to_addr: string, value: method transferTokens*(self: Controller, from_addr: string, to_addr: string, contractAddress: string, value: string, gas: string, gasPrice: string, maxPriorityFeePerGas: string,maxFeePerGas: string, - password: string, uuid: string): bool = + password: string, uuid: string +): bool = result = self.transactionService.transferTokens(from_addr, to_addr, contractAddress, value, gas, gasPrice, maxPriorityFeePerGas, maxFeePerGas, password, uuid) + +method baseFeePerGas*(self: Controller): string = + return self.transactionService.baseFeePerGas() + +method suggestedFees*(self: Controller): string = + let suggestedFees = self.transactionService.suggestedFees() + return suggestedFees.toJson() \ No newline at end of file diff --git a/src/app/modules/main/wallet_section/transactions/controller_interface.nim b/src/app/modules/main/wallet_section/transactions/controller_interface.nim index a39809d1d8..5150ca2d0b 100644 --- a/src/app/modules/main/wallet_section/transactions/controller_interface.nim +++ b/src/app/modules/main/wallet_section/transactions/controller_interface.nim @@ -43,7 +43,14 @@ method transferEth*(self: AccessInterface, from_addr: string, to_addr: string, v method transferTokens*(self: AccessInterface, from_addr: string, to_addr: string, contractAddress: string, value: string, gas: string, gasPrice: string, maxPriorityFeePerGas: string, maxFeePerGas: string, password: string, - uuid: string): bool {.base.} = + uuid: string +): bool {.base.} = + raise newException(ValueError, "No implementation available") + +method baseFeePerGas*(self: AccessInterface): string {.base.} = + raise newException(ValueError, "No implementation available") + +method suggestedFees*(self: AccessInterface): string {.base.} = raise newException(ValueError, "No implementation available") type diff --git a/src/app/modules/main/wallet_section/transactions/io_interface.nim b/src/app/modules/main/wallet_section/transactions/io_interface.nim index 86f6ec581e..9ab1cda352 100644 --- a/src/app/modules/main/wallet_section/transactions/io_interface.nim +++ b/src/app/modules/main/wallet_section/transactions/io_interface.nim @@ -57,6 +57,12 @@ method transferTokens*(self: AccessInterface, from_addr: string, to_addr: string method transactionWasSent*(self: AccessInterface, result: string) {.base.} = raise newException(ValueError, "No implementation available") +method baseFeePerGas*(self: AccessInterface): string {.base.} = + raise newException(ValueError, "No implementation available") + +method suggestedFees*(self: AccessInterface): string {.base.} = + raise newException(ValueError, "No implementation available") + # View Delegate Interface # Delegate for the view must be declared here due to use of QtObject and multi # inheritance, which is not well supported in Nim. diff --git a/src/app/modules/main/wallet_section/transactions/module.nim b/src/app/modules/main/wallet_section/transactions/module.nim index 9d36ae4bb2..8aed63477c 100644 --- a/src/app/modules/main/wallet_section/transactions/module.nim +++ b/src/app/modules/main/wallet_section/transactions/module.nim @@ -103,3 +103,8 @@ method transferTokens*(self: Module, from_addr: string, to_addr: string, contrac method transactionWasSent*(self: Module, result: string) = self.view.transactionWasSent(result) +method baseFeePerGas*(self: Module): string = + return self.controller.baseFeePerGas() + +method suggestedFees*(self: Module): string = + return self.controller.suggestedFees() diff --git a/src/app/modules/main/wallet_section/transactions/view.nim b/src/app/modules/main/wallet_section/transactions/view.nim index 8bbbaf6449..c187d3d5ef 100644 --- a/src/app/modules/main/wallet_section/transactions/view.nim +++ b/src/app/modules/main/wallet_section/transactions/view.nim @@ -4,6 +4,7 @@ import ./item import ./model import ./io_interface +import ../../../../../app_service/common/conversion as common_conversion import ../../../../../app_service/service/wallet_account/dto QtObject: @@ -123,3 +124,10 @@ QtObject: maxFeePerGas: string, password: string, uuid: string): bool {.slot.} = result = self.delegate.transferTokens(from_addr, to_addr, contractAddress, value, gas, gasPrice, maxPriorityFeePerGas, maxFeePerGas, password, uuid) + + proc latestBaseFeePerGas*(self: View): string {.slot.} = + let baseFeeWei = self.delegate.baseFeePerGas() + return common_conversion.wei2Gwei(baseFeeWei) + + proc suggestedFees*(self: View): string {.slot.} = + return self.delegate.suggestedFees() \ No newline at end of file diff --git a/src/app/modules/main/wallet_section/view.nim b/src/app/modules/main/wallet_section/view.nim index 2a08fb5140..d5562d94c1 100644 --- a/src/app/modules/main/wallet_section/view.nim +++ b/src/app/modules/main/wallet_section/view.nim @@ -72,3 +72,6 @@ QtObject: self.signingPhrase = signingPhrase self.isMnemonicBackedUp = mnemonicBackedUp self.currentCurrencyChanged() + + proc isEIP1559Enabled*(self: View): QVariant {.slot.} = + return newQVariant(self.delegate.isEIP1559Enabled()) \ No newline at end of file diff --git a/src/app_service/service/ens/service.nim b/src/app_service/service/ens/service.nim index b7843ae6ac..b7fd2aaebd 100644 --- a/src/app_service/service/ens/service.nim +++ b/src/app_service/service/ens/service.nim @@ -296,7 +296,7 @@ QtObject: try: let chainId = self.settingsService.getCurrentNetworkId() - eip1559Enabled = self.settingsService.isEIP1559Enabled() + eip1559Enabled = self.networkService.isEIP1559Enabled() txData = ens_utils.buildTransaction(parseAddress(address), 0.u256, gas, gasPrice, eip1559Enabled, maxPriorityFeePerGas, maxFeePerGas) @@ -385,7 +385,7 @@ QtObject: networkType = self.settingsService.getCurrentNetwork().toNetworkType() network = self.networkService.getNetwork(networkType) chainId = network.chainId - eip1559Enabled = self.settingsService.isEIP1559Enabled() + eip1559Enabled = self.networkService.isEIP1559Enabled() txData = ens_utils.buildTransaction(parseAddress(address), 0.u256, gas, gasPrice, eip1559Enabled, maxPriorityFeePerGas, maxFeePerGas) diff --git a/src/app_service/service/eth/utils.nim b/src/app_service/service/eth/utils.nim index 223f72f9ae..3a0d24ccac 100644 --- a/src/app_service/service/eth/utils.nim +++ b/src/app_service/service/eth/utils.nim @@ -146,6 +146,7 @@ proc validateTransactionInput*(from_addr, to_addr, assetAddress, value, gas, gas if not isAddress(to_addr): raise newException(ValueError, "to_addr is not a valid ETH address") if parseFloat(value) < 0: raise newException(ValueError, "value should be a number >= 0") if parseInt(gas) <= 0: raise newException(ValueError, "gas should be a number > 0") + if isEIP1599Enabled: if gasPrice != "" and (maxPriorityFeePerGas != "" or maxFeePerGas != ""): raise newException(ValueError, "gasPrice can't be used with maxPriorityFeePerGas and maxFeePerGas") diff --git a/src/app_service/service/network/service.nim b/src/app_service/service/network/service.nim index b1061aa95b..2a03de232c 100644 --- a/src/app_service/service/network/service.nim +++ b/src/app_service/service/network/service.nim @@ -84,4 +84,15 @@ method toggleNetwork*(self: Service, chainId: int) = let network = self.getNetwork(chainId) network.enabled = not network.enabled - self.upsertNetwork(network) \ No newline at end of file + self.upsertNetwork(network) + +method isEIP1559Enabled*(self: Service): bool = + # TODO: Assume multi network is not enabled + # TODO: add block number chain for other chains + let network = self.getEnabledNetworks()[0] + case network.chainId: + of 3: return true + of 4: return true + of 5: return true + of 1: return true + else: return false \ No newline at end of file diff --git a/src/app_service/service/network/service_interface.nim b/src/app_service/service/network/service_interface.nim index 5101011654..fe09398a28 100644 --- a/src/app_service/service/network/service_interface.nim +++ b/src/app_service/service/network/service_interface.nim @@ -31,4 +31,7 @@ method getNetwork*(self: ServiceInterface, chainId: int): NetworkDto {.base.} = raise newException(ValueError, "No implementation available") method toggleNetwork*(self: ServiceInterface, chainId: int) {.base.} = + raise newException(ValueError, "No implementation available") + +method isEIP1559Enabled*(self: ServiceInterface): bool {.base.} = raise newException(ValueError, "No implementation available") \ No newline at end of file diff --git a/src/app_service/service/settings/service.nim b/src/app_service/service/settings/service.nim index 4db1fb28ee..c925d116c4 100644 --- a/src/app_service/service/settings/service.nim +++ b/src/app_service/service/settings/service.nim @@ -393,23 +393,6 @@ method saveWalletVisibleTokens*(self: Service, visibleTokens: Table[int, seq[str return false -method isEIP1559Enabled*(self: Service, blockNumber: int): bool = - let networkId = self.getCurrentNetworkDetails().config.NetworkId - let activationBlock = case networkId: - of 3: 10499401 # Ropsten - of 4: 8897988 # Rinkeby - of 5: 5062605 # Goerli - of 1: 12965000 # Mainnet - else: -1 - if activationBlock > -1 and blockNumber >= activationBlock: - result = true - else: - result = false - self.eip1559Enabled = result - -method isEIP1559Enabled*(self: Service): bool = - result = self.eip1559Enabled - method getRecentStickers*(self: Service): seq[string] = result = self.settings.recentStickerHashes diff --git a/src/app_service/service/settings/service_interface.nim b/src/app_service/service/settings/service_interface.nim index ea9c8d3731..09c0d78fde 100644 --- a/src/app_service/service/settings/service_interface.nim +++ b/src/app_service/service/settings/service_interface.nim @@ -227,12 +227,6 @@ method getWalletVisibleTokens*(self: ServiceInterface): Table[int, seq[string]] method saveWalletVisibleTokens*(self: ServiceInterface, tokens: Table[int, seq[string]]): bool {.base.} = raise newException(ValueError, "No implementation available") -method isEIP1559Enabled*(self: ServiceInterface, blockNumber: int): bool {.base.} = - raise newException(ValueError, "No implementation available") - -method isEIP1559Enabled*(self: ServiceInterface): bool {.base.} = - raise newException(ValueError, "No implementation available") - method getRecentStickers*(self: ServiceInterface): seq[string] {.base.} = raise newException(ValueError, "No implementation available") diff --git a/src/app_service/service/stickers/service.nim b/src/app_service/service/stickers/service.nim index 900a576b97..d59e790877 100644 --- a/src/app_service/service/stickers/service.nim +++ b/src/app_service/service/stickers/service.nim @@ -156,7 +156,7 @@ QtObject: ) proc buy*(self: Service, packId: int, address: string, price: string, gas: string, gasPrice: string, maxPriorityFeePerGas: string, maxFeePerGas: string, password: string): tuple[response: string, success: bool] = - let eip1559Enabled = self.settingsService.isEIP1559Enabled() + let eip1559Enabled = self.networkService.isEIP1559Enabled() try: status_utils.validateTransactionInput(address, address, "", price, gas, gasPrice, "", eip1559Enabled, maxPriorityFeePerGas, maxFeePerGas, "ok") diff --git a/src/app_service/service/transaction/service.nim b/src/app_service/service/transaction/service.nim index 10207037f9..502e451952 100644 --- a/src/app_service/service/transaction/service.nim +++ b/src/app_service/service/transaction/service.nim @@ -1,10 +1,11 @@ -import NimQml, chronicles, sequtils, sugar, stint, strutils, json, strformat +import NimQml, chronicles, sequtils, sugar, stint, strutils, json, strformat, algorithm, math import ../../../backend/transactions as transactions import ../../../backend/wallet as status_wallet import ../../../backend/eth import ../ens/utils as ens_utils from ../../common/account_constants import ZERO_ADDRESS +import ../../common/conversion as common_conversion import ../../../app/core/[main] import ../../../app/core/signals/types @@ -48,6 +49,19 @@ type TransactionSentArgs* = ref object of Args result*: string +type SuggestedFees = object + gasPrice: string + baseFee: float + maxPriorityFeePerGas: float + maxFeePerGasL: float + maxFeePerGasM: float + maxFeePerGasH: float + +proc cmpUint256(x, y: Uint256): int = + if x > y: 1 + elif x == y: 0 + else: -1 + QtObject: type Service* = ref object of QObject events: EventEmitter @@ -57,6 +71,8 @@ QtObject: networkService: network_service.ServiceInterface settingsService: settings_service.ServiceInterface + baseFeePerGas: string + # Forward declaration proc checkPendingTransactions*(self: Service) proc checkPendingTransactions*(self: Service, address: string) @@ -71,7 +87,7 @@ QtObject: ethService: eth_service.ServiceInterface, networkService: network_service.ServiceInterface, settingsService: settings_service.ServiceInterface - ): Service = + ): Service = new(result, delete) result.QObject.setup result.events = events @@ -81,20 +97,25 @@ QtObject: result.networkService = networkService result.settingsService = settingsService + proc baseFeePerGas*(self: Service): string = + return self.baseFeePerGas + + proc setLatestBaseFeePerGas*(self: Service) = + let response = eth.getBlockByNumber("latest") + self.baseFeePerGas = $fromHex(Stuint[256], response.result{"baseFeePerGas"}.getStr) + proc doConnect*(self: Service) = self.events.on(SignalType.Wallet.event) do(e:Args): var data = WalletSignal(e) if(data.eventType == "newblock"): + self.setLatestBaseFeePerGas() + for acc in data.accounts: self.checkPendingTransactions(acc) - # TODO check if these need to be added back - # self.status.wallet.updateAccount(acc) - # discard self.status.wallet.isEIP1559Enabled(data.blockNumber) - # self.status.wallet.setLatestBaseFee(data.baseFeePerGas) - # self.view.updateView() proc init*(self: Service) = self.doConnect() + self.setLatestBaseFeePerGas() proc checkRecentHistory*(self: Service) = try: @@ -266,7 +287,7 @@ QtObject: uuid: string ): bool {.slot.} = try: - let eip1559Enabled = self.settingsService.isEIP1559Enabled() + let eip1559Enabled = self.networkService.isEIP1559Enabled() eth_utils.validateTransactionInput(from_addr, to_addr, assetAddress = "", value, gas, gasPrice, data = "", eip1559Enabled, maxPriorityFeePerGas, maxFeePerGas, uuid) @@ -277,6 +298,7 @@ QtObject: let json: JsonNode = %tx let response = eth.sendTransaction($json, password) + # only till it is moved to another thred # if response.error != nil: # raise newException(Exception, response.error.message) @@ -305,7 +327,7 @@ QtObject: uuid: string ): bool = try: - let eip1559Enabled = self.settingsService.isEIP1559Enabled() + let eip1559Enabled = self.networkService.isEIP1559Enabled() eth_utils.validateTransactionInput(from_addr, to_addr, assetAddress, value, gas, gasPrice, data = "", eip1559Enabled, maxPriorityFeePerGas, maxFeePerGas, uuid) @@ -332,3 +354,56 @@ QtObject: error "Error sending token transfer transaction", msg = e.msg return false return true + + proc maxPriorityFeePerGas(self: Service): Uint256 = + let response = eth.maxPriorityFeePerGas() + return fromHex(Stuint[256], response.result.getStr) + + proc getGasPrice(self: Service): Uint256 = + let response = eth.getGasPrice() + return fromHex(Stuint[256], response.result.getStr) + + proc feeHistory(self: Service, n:int): seq[Uint256] = + let response = eth.feeHistory(101) + for it in response.result["baseFeePerGas"]: + result.add(fromHex(Stuint[256], it.getStr)) + + result.sort(cmpUint256) + + proc suggestedFees*(self: Service): SuggestedFees = + #[ + 0. priority tip always same, the value returned by eth_maxPriorityFeePerGas + 1. slow fee 10th percentile base fee (last 100 blocks) + eth_maxPriorityFeePerGas + 2. normal fee. + if 20th_percentile <= current_base_fee <= 80th_percentile then fee = current_base_fee + eth_maxPriorityFeePerGas. + if current_base_fee < 20th_percentile then fee = 20th_percentile + eth_maxPriorityFeePerGas + if current_base_fee > 80th_percentile then fee = 80th_percentile + eth_maxPriorityFeePerGas + The idea is to avoid setting too low base fee when price is in a dip and also to avoid overpaying on peak. + Specific percentiles can be revisit later, it doesn't need to be symmetric because we are mostly interested in not getting stuck and overpaying might not be a huge issue here. + 3. fast fee: current_base_fee + eth_maxPriorityFeePerGas + ]# + + let maxPriorityFeePerGas = self.maxPriorityFeePerGas() + let feeHistory = self.feeHistory(101) + let baseFee = self.baseFeePerGas.u256 + let gasPrice = self.getGasPrice() + + let perc10 = feeHistory[ceil(10/100 * feeHistory.len.float).int - 1] + let perc20 = feeHistory[ceil(20/100 * feeHistory.len.float).int - 1] + let perc80 = feeHistory[ceil(80/100 * feeHistory.len.float).int - 1] + + let maxFeePerGasM = if baseFee >= perc20 and baseFee <= perc80: + baseFee + maxPriorityFeePerGas + elif baseFee < perc20: + perc20 + maxPriorityFeePerGas + else: + perc80 + maxPriorityFeePerGas + + return SuggestedFees( + gasPrice: $gasPrice, + baseFee: parseFloat(common_conversion.wei2gwei($baseFee)), + maxPriorityFeePerGas: parseFloat(common_conversion.wei2gwei($maxPriorityFeePerGas)), + maxFeePerGasL: parseFloat(common_conversion.wei2gwei($(perc10 + maxPriorityFeePerGas))), + maxFeePerGasM: parseFloat(common_conversion.wei2gwei($(maxFeePerGasM))), + maxFeePerGasH: parseFloat(common_conversion.wei2gwei($(baseFee + maxPriorityFeePerGas))) + ) diff --git a/src/backend/eth.nim b/src/backend/eth.nim index 4ec7f9734f..85de6ec1fc 100644 --- a/src/backend/eth.nim +++ b/src/backend/eth.nim @@ -29,3 +29,11 @@ proc getEthAccounts*(): RpcResponse[JsonNode] {.raises: [Exception].} = proc getGasPrice*(payload = %* []): RpcResponse[JsonNode] {.raises: [Exception].} = return core.callPrivateRPC("eth_gasPrice", payload) + +proc maxPriorityFeePerGas*(): RpcResponse[JsonNode] {.raises: [Exception].} = + let payload = %* [] + return core.callPrivateRPC("eth_maxPriorityFeePerGas", payload) + +proc feeHistory*(n: int): RpcResponse[JsonNode] {.raises: [Exception].} = + let payload = %* [n, "latest", nil] + return core.callPrivateRPC("eth_feeHistory", payload) \ No newline at end of file diff --git a/ui/app/AppLayouts/Browser/stores/WalletStore.qml b/ui/app/AppLayouts/Browser/stores/WalletStore.qml index 0466e5cc52..29d70dd81a 100644 --- a/ui/app/AppLayouts/Browser/stores/WalletStore.qml +++ b/ui/app/AppLayouts/Browser/stores/WalletStore.qml @@ -22,4 +22,8 @@ QtObject { // walletModel.gasView.getGasPrice() } + function isEIP1559Enabled() { + return walletSection.isEIP1559Enabled() + } + } diff --git a/ui/app/AppLayouts/Browser/views/WebProviderObj.qml b/ui/app/AppLayouts/Browser/views/WebProviderObj.qml index 4a89139e56..37a6cf0c3c 100644 --- a/ui/app/AppLayouts/Browser/views/WebProviderObj.qml +++ b/ui/app/AppLayouts/Browser/views/WebProviderObj.qml @@ -65,7 +65,7 @@ QtObject { // TODO: use bignumber instead of floats trx.value = RootStore.getEth2Hex(parseFloat(value)) trx.gas = "0x" + parseInt(selectedGasLimit, 10).toString(16) - if (walletModel.transactionsView.isEIP1559Enabled) { + if (WalletStore.isEIP1559Enabled()) { trx.maxPriorityFeePerGas = RootStore.getGwei2Hex(parseFloat(selectedTipLimit)) trx.maxFeePerGas = RootStore.getGwei2Hex(parseFloat(selectedOverallLimit)) } else { diff --git a/ui/app/AppLayouts/stores/RootStore.qml b/ui/app/AppLayouts/stores/RootStore.qml index 5bf87cc97f..23992bcb5f 100644 --- a/ui/app/AppLayouts/stores/RootStore.qml +++ b/ui/app/AppLayouts/stores/RootStore.qml @@ -91,4 +91,16 @@ QtObject { return walletSectionTransactions.transferTokens(from, to, address, amount, gasLimit, gasPrice, tipLimit, overallLimit, password, uuid); } + + function isEIP1559Enabled() { + return walletSection.isEIP1559Enabled() + } + + function latestBaseFeePerGas() { + return walletSectionTransactions.latestBaseFeePerGas() + } + + function suggestedFees() { + return JSON.parse(walletSectionTransactions.suggestedFees()) + } } diff --git a/ui/imports/shared/controls/GasSelector.qml b/ui/imports/shared/controls/GasSelector.qml index babd7374d2..5d092fc3d9 100644 --- a/ui/imports/shared/controls/GasSelector.qml +++ b/ui/imports/shared/controls/GasSelector.qml @@ -17,14 +17,16 @@ Item { property double gasPrice: 0 - // Not Refactored Yet - property bool eip1599Enabled: false //walletModel.transactionsView.isEIP1559Enabled - property var suggestedFees: "" //JSON.parse(walletModel.gasView.suggestedFees) - property var latestBaseFee: "" //JSON.parse(walletModel.transactionsView.latestBaseFee) + property bool isEIP1559Enabled: true + property var latestBaseFeePerGas: "" - property double latestBaseFeeGwei: { - if (!eip1599Enabled) return 0; - return parseFloat(latestBaseFee.gwei) + // Not Refactored Yet + property var suggestedFees: "" + + property double latestBaseFeePerGasGwei: { + if (!isEIP1559Enabled) return 0; + + return parseFloat(latestBaseFeePerGas) } property var getGasGweiValue: function () {} @@ -83,7 +85,7 @@ Item { function checkLimits(){ - if(!eip1599Enabled) return; + if(!isEIP1559Enabled) return; let inputTipLimit = parseFloat(inputPerGasTipLimit.text || "0.00") let inputOverallLimit = parseFloat(inputGasPrice.text || "0.00") @@ -110,15 +112,15 @@ Item { } // Per-gas overall limit rules - if(inputOverallLimit < latestBaseFeeGwei){ - errorMsg = appendError(errorMsg, qsTr("The limit is below the current base fee of %1 %2").arg(latestBaseFeeGwei).arg("Gwei")) + if(inputOverallLimit < latestBaseFeePerGasGwei){ + errorMsg = appendError(errorMsg, qsTr("The limit is below the current base fee of %1 %2").arg(latestBaseFeePerGasGwei).arg("Gwei")) showPriceLimitWarning = true } /* TODO: change these values false once EIP1559 suggestions are revised - else if((inputOverallLimit - inputTipLimit) < latestBaseFeeGwei){ + else if((inputOverallLimit - inputTipLimit) < latestBaseFeePerGasGwei){ errorMsg = appendError(errorMsg, qsTr("The limit should be at least %1 Gwei above the base fee").arg(perGasTipLimitFloor)) - } else if((inputOverallLimit - perGasTipLimitAverage) < latestBaseFeeGwei) { + } else if((inputOverallLimit - perGasTipLimitAverage) < latestBaseFeePerGasGwei) { errorMsg = appendError(errorMsg, qsTr("The maximum miner tip after the current base fee will be %1 Gwei, the minimum miner tip is currently %2 Gwei").arg(inputOverallLimit).arg(perGasTipLimitFloor), true) showTipLimitWarning = true }*/ @@ -201,11 +203,11 @@ Item { StyledText { id: baseFeeText - visible: eip1599Enabled && advancedMode + visible: isEIP1559Enabled && advancedMode anchors.top: parent.top anchors.left: prioritytext.right anchors.leftMargin: Style.current.smallPadding - text: qsTr("Current base fee: %1 %2").arg(latestBaseFeeGwei).arg("Gwei") + text: qsTr("Current base fee: %1 %2").arg(latestBaseFeePerGasGwei).arg("Gwei") font.weight: Font.Medium font.pixelSize: 13 color: Style.current.secondaryText @@ -241,7 +243,7 @@ Item { buttonGroup: gasGroup text: qsTr("Low") price: { - if (!eip1599Enabled) return gasPrice; + if (!isEIP1559Enabled) return gasPrice; return formatDec(suggestedFees.maxFeePerGasL, 6) } gasLimit: inputGasLimit ? inputGasLimit.text : "" @@ -249,7 +251,7 @@ Item { getFiatValue: root.getFiatValue defaultCurrency: root.defaultCurrency onChecked: { - if (eip1599Enabled){ + if (isEIP1559Enabled){ inputPerGasTipLimit.text = formatDec(suggestedFees.maxPriorityFeePerGas, 2); inputGasPrice.text = formatDec(suggestedFees.maxFeePerGasL, 2); } else { @@ -266,7 +268,7 @@ Item { //% "Optimal" text: qsTrId("optimal") price: { - if (!eip1599Enabled) { + if (!isEIP1559Enabled) { // Setting the gas price field here because the binding didn't work inputGasPrice.text = root.gasPrice return root.gasPrice @@ -279,7 +281,7 @@ Item { getFiatValue: root.getFiatValue defaultCurrency: root.defaultCurrency onChecked: { - if (eip1599Enabled){ + if (isEIP1559Enabled){ inputPerGasTipLimit.text = formatDec(suggestedFees.maxPriorityFeePerGas, 2); inputGasPrice.text = formatDec(suggestedFees.maxFeePerGasM, 2); } else { @@ -294,7 +296,7 @@ Item { buttonGroup: gasGroup text: qsTr("High") price: { - if (!eip1599Enabled) return gasPrice; + if (!isEIP1559Enabled) return gasPrice; return formatDec(suggestedFees.maxFeePerGasH,6); } gasLimit: inputGasLimit ? inputGasLimit.text : "" @@ -302,7 +304,7 @@ Item { getFiatValue: root.getFiatValue defaultCurrency: root.defaultCurrency onChecked: { - if (eip1599Enabled){ + if (isEIP1559Enabled){ inputPerGasTipLimit.text = formatDec(suggestedFees.maxPriorityFeePerGas, 2); inputGasPrice.text = formatDec(suggestedFees.maxFeePerGasH, 2); } else { @@ -331,7 +333,7 @@ Item { customHeight: 56 anchors.top: parent.top anchors.left: parent.left - anchors.right: eip1599Enabled ? inputPerGasTipLimit.left : inputGasPrice.left + anchors.right: isEIP1559Enabled ? inputPerGasTipLimit.left : inputGasPrice.left anchors.rightMargin: Style.current.padding placeholderText: "21000" validator: IntValidator{ @@ -355,7 +357,7 @@ Item { anchors.right: inputGasPrice.left anchors.rightMargin: Style.current.padding anchors.left: undefined - visible: eip1599Enabled + visible: isEIP1559Enabled width: 125 customHeight: 56 text: formatDec(suggestedFees.maxPriorityFeePerGas, 2); @@ -372,7 +374,7 @@ Item { color: Style.current.secondaryText //% "Gwei" text: qsTrId("gwei") - visible: eip1599Enabled + visible: isEIP1559Enabled anchors.top: parent.top anchors.topMargin: 42 anchors.right: inputPerGasTipLimit.right diff --git a/ui/imports/shared/popups/SendModal.qml b/ui/imports/shared/popups/SendModal.qml index 3ea200c76f..cdb8b7d070 100644 --- a/ui/imports/shared/popups/SendModal.qml +++ b/ui/imports/shared/popups/SendModal.qml @@ -45,7 +45,7 @@ ModalPopup { selectRecipient.selectedRecipient.address, txtAmount.selectedAmount, gasSelector.selectedGasLimit, - gasSelector.eip1599Enabled ? "" : gasSelector.selectedGasPrice, + gasSelector.isEIP1559Enabled ? "" : gasSelector.selectedGasPrice, gasSelector.selectedTipLimit, gasSelector.selectedOverallLimit, transactionSigner.enteredPassword, @@ -57,7 +57,7 @@ ModalPopup { txtAmount.selectedAsset.address, txtAmount.selectedAmount, gasSelector.selectedGasLimit, - gasSelector.eip1599Enabled ? "" : gasSelector.selectedGasPrice, + gasSelector.isEIP1559Enabled ? "" : gasSelector.selectedGasPrice, gasSelector.selectedTipLimit, gasSelector.selectedOverallLimit, transactionSigner.enteredPassword, @@ -147,6 +147,9 @@ ModalPopup { getGasEthValue: root.store.getGasEthValue getFiatValue: root.store.getFiatValue defaultCurrency: root.store.currentCurrency + isEIP1559Enabled: root.store.isEIP1559Enabled() + latestBaseFeePerGas: root.store.latestBaseFeePerGas() + suggestedFees: root.store.suggestedFees() width: stack.width property var estimateGas: Backpressure.debounce(gasSelector, 600, function() { @@ -265,16 +268,16 @@ ModalPopup { return root.sendTransaction() } - if(gasSelector.eip1599Enabled && stack.currentGroup === group2 && gasSelector.advancedMode){ + if(gasSelector.isEIP1559Enabled && stack.currentGroup === group2 && gasSelector.advancedMode){ if(gasSelector.showPriceLimitWarning || gasSelector.showTipLimitWarning){ Global.openPopup(transactionSettingsConfirmationPopupComponent, { - currentBaseFee: gasSelector.latestBaseFeeGwei, + currentBaseFee: gasSelector.latestBaseFeePerGasGwei, currentMinimumTip: gasSelector.perGasTipLimitFloor, currentAverageTip: gasSelector.perGasTipLimitAverage, tipLimit: gasSelector.selectedTipLimit, suggestedTipLimit: gasSelector.perGasTipLimitFloor, priceLimit: gasSelector.selectedOverallLimit, - suggestedPriceLimit: gasSelector.latestBaseFeeGwei + gasSelector.perGasTipLimitFloor, + suggestedPriceLimit: gasSelector.latestBaseFeePerGasGwei + gasSelector.perGasTipLimitFloor, showPriceLimitWarning: gasSelector.showPriceLimitWarning, showTipLimitWarning: gasSelector.showTipLimitWarning, onConfirm: function(){ diff --git a/ui/imports/shared/popups/SignTransactionModal.qml b/ui/imports/shared/popups/SignTransactionModal.qml index 973e2207c2..2bcdf3a349 100644 --- a/ui/imports/shared/popups/SignTransactionModal.qml +++ b/ui/imports/shared/popups/SignTransactionModal.qml @@ -45,7 +45,7 @@ StatusModal { selectRecipient.selectedRecipient.address, root.selectedAmount, gasSelector.selectedGasLimit, - gasSelector.eip1599Enabled ? "" : gasSelector.selectedGasPrice, + gasSelector.isEIP1559Enabled ? "" : gasSelector.selectedGasPrice, gasSelector.selectedTipLimit, gasSelector.selectedOverallLimit, transactionSigner.enteredPassword, @@ -57,7 +57,7 @@ StatusModal { root.selectedAsset.address, root.selectedAmount, gasSelector.selectedGasLimit, - gasSelector.eip1599Enabled ? "" : gasSelector.selectedGasPrice, + gasSelector.isEIP1559Enabled ? "" : gasSelector.selectedGasPrice, gasSelector.selectedTipLimit, gasSelector.selectedOverallLimit, transactionSigner.enteredPassword, @@ -156,6 +156,9 @@ StatusModal { getGasEthValue: root.store.getGasEthValue getFiatValue: root.store.getFiatValue defaultCurrency: root.store.currentCurrency + isEIP1559Enabled: root.store.isEIP1559Enabled() + latestBaseFeePerGas: root.store.latestBaseFeePerGas() + suggestedFees: root.store.suggestedFees() width: stack.width property var estimateGas: Backpressure.debounce(gasSelector, 600, function() { @@ -295,22 +298,22 @@ StatusModal { if (validity.isValid && !validity.isPending) { if (stack.isLastGroup) { return root.sendTransaction(gasSelector.selectedGasLimit, - gasSelector.eip1599Enabled ? "" : gasSelector.selectedGasPrice, + gasSelector.isEIP1559Enabled ? "" : gasSelector.selectedGasPrice, gasSelector.selectedTipLimit, gasSelector.selectedOverallLimit, transactionSigner.enteredPassword) } - if(gasSelector.eip1599Enabled && stack.currentGroup === groupSelectGas && gasSelector.advancedMode){ + if(gasSelector.isEIP1559Enabled && stack.currentGroup === groupSelectGas && gasSelector.advancedMode){ if(gasSelector.showPriceLimitWarning || gasSelector.showTipLimitWarning){ Global.openPopup(transactionSettingsConfirmationPopupComponent, { - currentBaseFee: gasSelector.latestBaseFeeGwei, + currentBaseFee: gasSelector.latestBaseFeePerGasGwei, currentMinimumTip: gasSelector.perGasTipLimitFloor, currentAverageTip: gasSelector.perGasTipLimitAverage, tipLimit: gasSelector.selectedTipLimit, suggestedTipLimit: gasSelector.perGasTipLimitFloor, // TODO: priceLimit: gasSelector.selectedOverallLimit, - suggestedPriceLimit: gasSelector.latestBaseFeeGwei + gasSelector.perGasTipLimitFloor, + suggestedPriceLimit: gasSelector.latestBaseFeePerGasGwei + gasSelector.perGasTipLimitFloor, showPriceLimitWarning: gasSelector.showPriceLimitWarning, showTipLimitWarning: gasSelector.showTipLimitWarning, onConfirm: function(){ diff --git a/ui/imports/shared/status/StatusETHTransactionModal.qml b/ui/imports/shared/status/StatusETHTransactionModal.qml index 158a11daa6..44eadf1fb2 100644 --- a/ui/imports/shared/status/StatusETHTransactionModal.qml +++ b/ui/imports/shared/status/StatusETHTransactionModal.qml @@ -35,7 +35,7 @@ ModalPopup { let responseStr = root.ensUsernamesStore.setPubKey(root.ensUsername, selectFromAccount.selectedAccount.address, gasSelector.selectedGasLimit, - gasSelector.eip1599Enabled ? "" : gasSelector.selectedGasPrice, + gasSelector.isEIP1559Enabled ? "" : gasSelector.selectedGasPrice, gasSelector.selectedTipLimit, gasSelector.selectedOverallLimit, transactionSigner.enteredPassword) @@ -120,7 +120,10 @@ ModalPopup { getGasEthValue: root.ensUsernamesStore.getGasEthValue getFiatValue: root.ensUsernamesStore.getFiatValue defaultCurrency: root.ensUsernamesStore.getCurrentCurrency() - + isEIP1559Enabled: root.store.isEIP1559Enabled() + latestBaseFeePerGas: root.store.latestBaseFeePerGas() + suggestedFees: root.store.suggestedFees() + property var estimateGas: Backpressure.debounce(gasSelector, 600, function() { let estimatedGas = root.estimateGasFunction(selectFromAccount.selectedAccount); gasSelector.selectedGasLimit = estimatedGas @@ -215,16 +218,16 @@ ModalPopup { return root.sendTransaction() } - if(gasSelector.eip1599Enabled && stack.currentGroup === group2 && gasSelector.advancedMode){ + if(gasSelector.isEIP1559Enabled && stack.currentGroup === group2 && gasSelector.advancedMode){ if(gasSelector.showPriceLimitWarning || gasSelector.showTipLimitWarning){ Global.openPopup(transactionSettingsConfirmationPopupComponent, { - currentBaseFee: gasSelector.latestBaseFeeGwei, + currentBaseFee: gasSelector.latestBaseFeePerGasGwei, currentMinimumTip: gasSelector.perGasTipLimitFloor, currentAverageTip: gasSelector.perGasTipLimitAverage, tipLimit: gasSelector.selectedTipLimit, suggestedTipLimit: gasSelector.perGasTipLimitFloor, priceLimit: gasSelector.selectedOverallLimit, - suggestedPriceLimit: gasSelector.latestBaseFeeGwei + gasSelector.perGasTipLimitFloor, + suggestedPriceLimit: gasSelector.latestBaseFeePerGasGwei + gasSelector.perGasTipLimitFloor, showPriceLimitWarning: gasSelector.showPriceLimitWarning, showTipLimitWarning: gasSelector.showTipLimitWarning, onConfirm: function(){ diff --git a/ui/imports/shared/status/StatusSNTTransactionModal.qml b/ui/imports/shared/status/StatusSNTTransactionModal.qml index f78d9fba68..c580fe764d 100644 --- a/ui/imports/shared/status/StatusSNTTransactionModal.qml +++ b/ui/imports/shared/status/StatusSNTTransactionModal.qml @@ -52,7 +52,7 @@ ModalPopup { function sendTransaction() { let responseStr = onSendTransaction(selectFromAccount.selectedAccount.address, gasSelector.selectedGasLimit, - gasSelector.eip1599Enabled ? "" : gasSelector.selectedGasPrice, + gasSelector.isEIP1559Enabled ? "" : gasSelector.selectedGasPrice, gasSelector.selectedTipLimit, gasSelector.selectedOverallLimit, transactionSigner.enteredPassword); @@ -127,6 +127,9 @@ ModalPopup { getGasEthValue: root.store.getGasEthValue getFiatValue: root.store.getFiatValue defaultCurrency: root.store.getCurrentCurrency() + isEIP1559Enabled: root.store.isEIP1559Enabled() + latestBaseFeePerGas: root.store.latestBaseFeePerGas() + suggestedFees: root.store.suggestedFees() width: stack.width property var estimateGas: Backpressure.debounce(gasSelector, 600, function() { @@ -228,16 +231,16 @@ ModalPopup { return root.sendTransaction() } - if(gasSelector.eip1599Enabled && stack.currentGroup === group2 && gasSelector.advancedMode){ + if(gasSelector.isEIP1559Enabled && stack.currentGroup === group2 && gasSelector.advancedMode){ if(gasSelector.showPriceLimitWarning || gasSelector.showTipLimitWarning){ Global.openPopup(transactionSettingsConfirmationPopupComponent, { - currentBaseFee: gasSelector.latestBaseFeeGwei, + currentBaseFee: gasSelector.latestBaseFeePerGasGwei, currentMinimumTip: gasSelector.perGasTipLimitFloor, currentAverageTip: gasSelector.perGasTipLimitAverage, tipLimit: gasSelector.selectedTipLimit, suggestedTipLimit: gasSelector.perGasTipLimitFloor, // TODO: priceLimit: gasSelector.selectedOverallLimit, - suggestedPriceLimit: gasSelector.latestBaseFeeGwei + gasSelector.perGasTipLimitFloor, + suggestedPriceLimit: gasSelector.latestBaseFeePerGasGwei + gasSelector.perGasTipLimitFloor, showPriceLimitWarning: gasSelector.showPriceLimitWarning, showTipLimitWarning: gasSelector.showTipLimitWarning, onConfirm: function(){