refactor(@desktop/settings-ens-usernames): ens usernames module added and corresponding ui updated

This commit is contained in:
Sale Djenic 2022-01-17 09:56:44 +01:00
parent bd2bb8952a
commit 69fc5bec5e
55 changed files with 2124 additions and 519 deletions

View File

@ -30,6 +30,7 @@ import ../../app_service/service/saved_address/service as saved_address_service
import ../../app_service/service/devices/service as devices_service
import ../../app_service/service/mailservers/service as mailservers_service
import ../../app_service/service/gif/service as gif_service
import ../../app_service/service/ens/service as ens_service
import ../modules/startup/module as startup_module
import ../modules/main/module as main_module
@ -82,6 +83,7 @@ type
mailserversService: mailservers_service.Service
nodeService: node_service.Service
gifService: gif_service.Service
ensService: ens_service.Service
# Modules
startupModule: startup_module.AccessInterface
@ -168,6 +170,9 @@ proc newAppController*(statusFoundation: StatusFoundation): AppController =
result.nodeService = node_service.newService(statusFoundation.events, statusFoundation.threadpool,
result.settingsService)
result.gifService = gif_service.newService(result.settingsService)
result.ensService = ens_service.newService(statusFoundation.events, statusFoundation.threadpool,
result.settingsService, result.walletAccountService, result.transactionService, result.ethService,
result.networkService, result.tokenService)
# Modules
result.startupModule = startup_module.newModule[AppController](
@ -206,6 +211,7 @@ proc newAppController*(statusFoundation: StatusFoundation): AppController =
result.mailserversService,
result.nodeService,
result.gifService,
result.ensService
)
# Do connections
@ -252,6 +258,7 @@ proc delete*(self: AppController) =
self.privacyService.delete
self.profileService.delete
self.generalService.delete
self.ensService.delete
proc startupDidLoad*(self: AppController) =
singletonInstance.engine.setRootContextProperty("localAppSettings", self.localAppSettingsVariant)
@ -294,6 +301,7 @@ proc load(self: AppController) =
self.aboutService.init()
self.devicesService.init()
self.mailserversService.init()
self.ensService.init()
let pubKey = self.settingsService.getPublicKey()
singletonInstance.localAccountSensitiveSettings.setFileName(pubKey)

View File

@ -43,6 +43,7 @@ import ../../../app_service/service/node_configuration/service_interface as node
import ../../../app_service/service/devices/service as devices_service
import ../../../app_service/service/mailservers/service as mailservers_service
import ../../../app_service/service/gif/service as gif_service
import ../../../app_service/service/ens/service as ens_service
import ../../core/eventemitter
@ -96,6 +97,7 @@ proc newModule*[T](
mailserversService: mailservers_service.Service,
nodeService: node_service.Service,
gifService: gif_service.Service,
ensService: ens_service.Service
): Module[T] =
result = Module[T]()
result.delegate = delegate
@ -128,7 +130,7 @@ proc newModule*[T](
dappPermissionsService, providerService)
result.profileSectionModule = profile_section_module.newModule(result, events, accountsService, settingsService,
profileService, contactsService, aboutService, languageService, privacyService, nodeConfigurationService,
devicesService, mailserversService, chatService)
devicesService, mailserversService, chatService, ensService, walletAccountService)
result.stickersModule = stickers_module.newModule(result, events, stickersService)
result.activityCenterModule = activity_center_module.newModule(result, events, activityCenterService, contactsService,
messageService)

View File

@ -0,0 +1,129 @@
import Tables, chronicles
import controller_interface
import io_interface
import ../../../../global/global_singleton
import ../../../../core/eventemitter
import ../../../../../app_service/service/settings/service_interface as settings_service
import ../../../../../app_service/service/ens/service as ens_service
import ../../../../../app_service/service/wallet_account/service_interface as wallet_account_service
export controller_interface
logScope:
topics = "profile-section-ens-usernames-module-controller"
type
Controller* = ref object of controller_interface.AccessInterface
delegate: io_interface.AccessInterface
events: EventEmitter
settingsService: settings_service.ServiceInterface
ensService: ens_service.Service
walletAccountService: wallet_account_service.ServiceInterface
proc newController*(delegate: io_interface.AccessInterface, events: EventEmitter,
settingsService: settings_service.ServiceInterface, ensService: ens_service.Service,
walletAccountService: wallet_account_service.ServiceInterface): Controller =
result = Controller()
result.delegate = delegate
result.events = events
result.settingsService = settingsService
result.ensService = ensService
result.walletAccountService = walletAccountService
method delete*(self: Controller) =
discard
method init*(self: Controller) =
self.events.on(SIGNAL_ENS_USERNAME_AVAILABILITY_CHECKED) do(e:Args):
let args = EnsUsernameAvailabilityArgs(e)
self.delegate.ensUsernameAvailabilityChecked(args.availabilityStatus)
self.events.on(SIGNAL_ENS_USERNAME_DETAILS_FETCHED) do(e:Args):
let args = EnsUsernameDetailsArgs(e)
self.delegate.onDetailsForEnsUsername(args.ensUsername, args.address, args.pubkey, args.isStatus, args.expirationTime)
self.events.on(SIGNAL_GAS_PRICE_FETCHED) do(e:Args):
let args = GasPriceArgs(e)
self.delegate.gasPriceFetched(args.gasPrice)
self.events.on(SIGNAL_ENS_TRANSACTION_CONFIRMED) do(e:Args):
let args = EnsTransactionArgs(e)
self.delegate.ensTransactionConfirmed(args.transactionType, args.ensUsername, args.transactionHash)
self.events.on(SIGNAL_ENS_TRANSACTION_REVERTED) do(e:Args):
let args = EnsTransactionArgs(e)
self.delegate.ensTransactionReverted(args.transactionType, args.ensUsername, args.transactionHash, args.revertReason)
method checkEnsUsernameAvailability*(self: Controller, desiredEnsUsername: string, statusDomain: bool) =
self.ensService.checkEnsUsernameAvailability(desiredEnsUsername, statusDomain)
method getMyPendingEnsUsernames*(self: Controller): seq[string] =
return self.ensService.getMyPendingEnsUsernames()
method getAllMyEnsUsernames*(self: Controller, includePendingEnsUsernames: bool): seq[string] =
return self.ensService.getAllMyEnsUsernames(includePendingEnsUsernames)
method fetchDetailsForEnsUsername*(self: Controller, ensUsername: string) =
self.ensService.fetchDetailsForEnsUsername(ensUsername)
method fetchGasPrice*(self: Controller) =
self.ensService.fetchGasPrice()
method setPubKeyGasEstimate*(self: Controller, ensUsername: string, address: string): int =
return self.ensService.setPubKeyGasEstimate(ensUsername, address)
method setPubKey*(self: Controller, ensUsername: string, address: string, gas: string, gasPrice: string,
maxPriorityFeePerGas: string, maxFeePerGas: string, password: string): string =
return self.ensService.setPubKey(ensUsername, address, gas, gasPrice, maxPriorityFeePerGas, maxFeePerGas, password)
method getCurrentNetworkDetails*(self: Controller): Network =
return self.settingsService.getCurrentNetworkDetails()
method getSigningPhrase*(self: Controller): string =
return self.settingsService.getSigningPhrase()
method saveNewEnsUsername*(self: Controller, ensUsername: string): bool =
return self.settingsService.saveNewEnsUsername(ensUsername)
method getPreferredEnsUsername*(self: Controller): string =
return self.settingsService.getPreferredName()
method releaseEnsEstimate*(self: Controller, ensUsername: string, address: string): int =
return self.ensService.releaseEnsEstimate(ensUsername, address)
method release*(self: Controller, ensUsername: string, address: string, gas: string, gasPrice: string, password: string):
string =
return self.ensService.release(ensUsername, address, gas, gasPrice, password)
method setPreferredName*(self: Controller, preferredName: string) =
if(self.settingsService.savePreferredName(preferredName)):
singletonInstance.userProfile.setPreferredName(preferredName)
else:
info "an error occurred saving prefered ens username", methodName="setPreferredName"
method getEnsRegisteredAddress*(self: Controller): string =
return self.ensService.getEnsRegisteredAddress()
method registerEnsGasEstimate*(self: Controller, ensUsername: string, address: string): int =
return self.ensService.registerEnsGasEstimate(ensUsername, address)
method registerEns*(self: Controller, ensUsername: string, address: string, gas: string, gasPrice: string,
maxPriorityFeePerGas: string, maxFeePerGas: string, password: string): string =
return self.ensService.registerEns(ensUsername, address, gas, gasPrice, maxPriorityFeePerGas, maxFeePerGas, password)
method getSNTBalance*(self: Controller): string =
return self.ensService.getSNTBalance()
method getWalletDefaultAddress*(self: Controller): string =
return self.walletAccountService.getWalletAccount(0).address
method getCurrentCurrency*(self: Controller): string =
return self.settingsService.getCurrency()
method getPrice*(self: Controller, crypto: string, fiat: string): float64 =
return self.walletAccountService.getPrice(crypto, fiat)
method getStatusToken*(self: Controller): string =
return self.ensService.getStatusToken()

View File

@ -0,0 +1,80 @@
import ../../../../../app_service/service/settings/dto/settings as settings_dto
type
AccessInterface* {.pure inheritable.} = ref object of RootObj
## Abstract class for any input/interaction with this module.
method delete*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method init*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method checkEnsUsernameAvailability*(self: AccessInterface, desiredEnsUsername: string, statusDomain: bool) {.base.} =
raise newException(ValueError, "No implementation available")
method getMyPendingEnsUsernames*(self: AccessInterface): seq[string] {.base.} =
raise newException(ValueError, "No implementation available")
method getAllMyEnsUsernames*(self: AccessInterface, includePendingEnsUsernames: bool): seq[string] {.base.} =
raise newException(ValueError, "No implementation available")
method fetchDetailsForEnsUsername*(self: AccessInterface, ensUsername: string) {.base.} =
raise newException(ValueError, "No implementation available")
method fetchGasPrice*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method setPubKeyGasEstimate*(self: AccessInterface, ensUsername: string, address: string): int {.base.} =
raise newException(ValueError, "No implementation available")
method setPubKey*(self: AccessInterface, ensUsername: string, address: string, gas: string, gasPrice: string,
maxPriorityFeePerGas: string, maxFeePerGas: string, password: string): string {.base.} =
raise newException(ValueError, "No implementation available")
method getCurrentNetworkDetails*(self: AccessInterface): Network {.base.} =
raise newException(ValueError, "No implementation available")
method getSigningPhrase*(self: AccessInterface): string {.base.} =
raise newException(ValueError, "No implementation available")
method saveNewEnsUsername*(self: AccessInterface, ensUsername: string): bool {.base.} =
raise newException(ValueError, "No implementation available")
method getPreferredEnsUsername*(self: AccessInterface): string {.base.} =
raise newException(ValueError, "No implementation available")
method releaseEnsEstimate*(self: AccessInterface, ensUsername: string, address: string): int {.base.} =
raise newException(ValueError, "No implementation available")
method release*(self: AccessInterface, ensUsername: string, address: string, gas: string, gasPrice: string,
password: string): string {.base.} =
raise newException(ValueError, "No implementation available")
method setPreferredName*(self: AccessInterface, preferredName: string) {.base.} =
raise newException(ValueError, "No implementation available")
method getEnsRegisteredAddress*(self: AccessInterface): string {.base.} =
raise newException(ValueError, "No implementation available")
method registerEnsGasEstimate*(self: AccessInterface, ensUsername: string, address: string): int {.base.} =
raise newException(ValueError, "No implementation available")
method registerEns*(self: AccessInterface, ensUsername: string, address: string, gas: string, gasPrice: string,
maxPriorityFeePerGas: string, maxFeePerGas: string, password: string): string {.base.} =
raise newException(ValueError, "No implementation available")
method getSNTBalance*(self: AccessInterface): string {.base.} =
raise newException(ValueError, "No implementation available")
method getWalletDefaultAddress*(self: AccessInterface): string {.base.} =
raise newException(ValueError, "No implementation available")
method getCurrentCurrency*(self: AccessInterface): string {.base.} =
raise newException(ValueError, "No implementation available")
method getPrice*(self: AccessInterface, crypto: string, fiat: string): float64 {.base.} =
raise newException(ValueError, "No implementation available")
method getStatusToken*(self: AccessInterface): string {.base.} =
raise newException(ValueError, "No implementation available")

View File

@ -0,0 +1,61 @@
import NimQml, Tables, strformat
type
ModelRole {.pure.} = enum
Id = UserRole + 1
Name
QtObject:
type
Model* = ref object of QAbstractListModel
items: seq[tuple[id: string, name: string]]
proc delete*(self: Model) =
self.items = @[]
self.QAbstractListModel.delete
proc setup(self: Model) =
self.QAbstractListModel.setup
proc newModel*(): Model =
new(result, delete)
result.setup
proc `$`*(self: Model): string =
for i in 0 ..< self.items.len:
result &= fmt"""
[{i}]:(id: {self.items[i].id}, name: {self.items[i].name})
"""
method rowCount(self: Model, index: QModelIndex = nil): int =
return self.items.len
method roleNames(self: Model): Table[int, string] =
{
ModelRole.Id.int:"id",
ModelRole.Name.int:"name",
}.toTable
method data(self: Model, index: QModelIndex, role: int): QVariant =
if (not index.isValid):
return
if (index.row < 0 or index.row >= self.items.len):
return
let item = self.items[index.row]
let enumRole = role.ModelRole
case enumRole:
of ModelRole.Id:
result = newQVariant(item.id)
of ModelRole.Name:
result = newQVariant(item.name)
proc add*(self: Model, id: string, name: string) =
let parentModelIndex = newQModelIndex()
defer: parentModelIndex.delete
self.beginInsertRows(parentModelIndex, self.items.len, self.items.len)
self.items.add((id: id, name: name))
self.endInsertRows()

View File

@ -0,0 +1,11 @@
# Defines how parent module accesses this module
include ./private_interfaces/module_base_interface
include ./private_interfaces/module_access_interface
# Defines how this module view communicates with this module
include ./private_interfaces/module_view_delegate_interface
# Defines how this controller communicates with this module
include ./private_interfaces/module_controller_delegate_interface
# Defines how submodules of this module communicate with this module

View File

@ -0,0 +1,102 @@
import NimQml, Tables
type Item* = object
ensUsername*: string
isPending*: bool
type
ModelRole {.pure.} = enum
EnsUsername = UserRole + 1
IsPending
QtObject:
type
Model* = ref object of QAbstractListModel
items: seq[Item]
proc delete(self: Model) =
self.items = @[]
self.QAbstractListModel.delete
proc setup(self: Model) =
self.QAbstractListModel.setup
proc newModel*(): Model =
new(result, delete)
result.setup
proc countChanged(self: Model) {.signal.}
proc getCount(self: Model): int {.slot.} =
self.items.len
QtProperty[int] count:
read = getCount
notify = countChanged
method rowCount(self: Model, index: QModelIndex = nil): int =
return self.items.len
method roleNames(self: Model): Table[int, string] =
{
ModelRole.EnsUsername.int:"ensUsername",
ModelRole.IsPending.int:"isPending"
}.toTable
method data(self: Model, index: QModelIndex, role: int): QVariant =
if (not index.isValid):
return
if (index.row < 0 or index.row >= self.items.len):
return
let item = self.items[index.row]
let enumRole = role.ModelRole
case enumRole:
of ModelRole.EnsUsername:
result = newQVariant(item.ensUsername)
of ModelRole.IsPending:
result = newQVariant(item.isPending)
proc findIndexForItemWithEnsUsername(self: Model, ensUsername: string): int =
for i in 0 ..< self.items.len:
if(self.items[i].ensUsername == ensUsername):
return i
return -1
proc containsEnsUsername*(self: Model, ensUsername: string): bool =
return self.findIndexForItemWithEnsUsername(ensUsername) != -1
proc addItem*(self: Model, item: Item) =
if(self.containsEnsUsername(item.ensUsername)):
return
let parentModelIndex = newQModelIndex()
defer: parentModelIndex.delete
self.beginInsertRows(parentModelIndex, self.items.len, self.items.len)
self.items.add(item)
self.endInsertRows()
self.countChanged()
proc removeItemByEnsUsername*(self: Model, ensUsername: string) =
let index = self.findIndexForItemWithEnsUsername(ensUsername)
if(index == -1):
return
let parentModelIndex = newQModelIndex()
defer: parentModelIndex.delete
self.beginRemoveRows(parentModelIndex, index, index)
self.items.delete(index)
self.endRemoveRows()
self.countChanged()
proc updatePendingStatus*(self: Model, ensUsername: string, pendingStatus: bool) =
let ind = self.findIndexForItemWithEnsUsername(ensUsername)
if(ind == -1):
return
self.items[ind].isPending = pendingStatus
let index = self.createIndex(ind, 0, nil)
self.dataChanged(index, index, @[ModelRole.IsPending.int])

View File

@ -0,0 +1,242 @@
import NimQml, json, stint, strutils, strformat, parseutils, chronicles
import io_interface
import ../io_interface as delegate_interface
import view, controller, model
import ../../../../core/eventemitter
import ../../../../../app_service/common/conversion as service_conversion
import ../../../../../app_service/service/settings/service_interface as settings_service
import ../../../../../app_service/service/ens/service as ens_service
import ../../../../../app_service/service/ens/utils as ens_utils
import ../../../../../app_service/service/wallet_account/service_interface as wallet_account_service
export io_interface
logScope:
topics = "profile-section-ens-usernames-module"
include ../../../../../app_service/common/json_utils
type
Module* = ref object of io_interface.AccessInterface
delegate: delegate_interface.AccessInterface
view: View
viewVariant: QVariant
controller: controller.AccessInterface
moduleLoaded: bool
proc newModule*(delegate: delegate_interface.AccessInterface, events: EventEmitter,
settingsService: settings_service.ServiceInterface, ensService: ens_service.Service,
walletAccountService: wallet_account_service.ServiceInterface): Module =
result = Module()
result.delegate = delegate
result.view = view.newView(result)
result.viewVariant = newQVariant(result.view)
result.controller = controller.newController(result, events, settingsService, ensService, walletAccountService)
result.moduleLoaded = false
method delete*(self: Module) =
self.view.delete
self.viewVariant.delete
self.controller.delete
method load*(self: Module) =
self.controller.init()
let signingPhrase = self.controller.getSigningPhrase()
let network = self.controller.getCurrentNetworkDetails()
var link = network.etherscanLink.replace("/address", "/tx")
self.view.load(link, signingPhrase)
method isLoaded*(self: Module): bool =
return self.moduleLoaded
method viewDidLoad*(self: Module) =
self.fetchGasPrice()
# add registered ens usernames
let registeredEnsUsernames = self.controller.getAllMyEnsUsernames(includePendingEnsUsernames = false)
for u in registeredEnsUsernames:
self.view.model().addItem(Item(ensUsername: u, isPending: false))
# add pending ens usernames
let pendingEnsUsernames = self.controller.getMyPendingEnsUsernames()
for u in pendingEnsUsernames:
self.view.model().addItem(Item(ensUsername: u, isPending: true))
self.moduleLoaded = true
self.delegate.ensUsernamesModuleDidLoad()
method getModuleAsVariant*(self: Module): QVariant =
return self.viewVariant
method checkEnsUsernameAvailability*(self: Module, desiredEnsUsername: string, statusDomain: bool) =
self.controller.checkEnsUsernameAvailability(desiredEnsUsername, statusDomain)
method ensUsernameAvailabilityChecked*(self: Module, availabilityStatus: string) =
self.view.sendUsernameAvailabilityCheckedSignal(availabilityStatus)
method numOfPendingEnsUsernames*(self: Module): int =
return self.controller.getMyPendingEnsUsernames().len
method fetchDetailsForEnsUsername*(self: Module, ensUsername: string) =
self.controller.fetchDetailsForEnsUsername(ensUsername)
method onDetailsForEnsUsername*(self: Module, ensUsername: string, address: string, pubkey: string, isStatus: bool,
expirationTime: int) =
self.view.setDetailsForEnsUsername(ensUsername, address, pubkey, isStatus, expirationTime)
method fetchGasPrice*(self: Module) =
self.controller.fetchGasPrice()
method gasPriceFetched*(self: Module, gasPrice: string) =
self.view.setGasPrice(gasPrice)
method setPubKeyGasEstimate*(self: Module, ensUsername: string, address: string): int =
return self.controller.setPubKeyGasEstimate(ensUsername, address)
method setPubKey*(self: Module, ensUsername: string, address: string, gas: string, gasPrice: string,
maxPriorityFeePerGas: string, maxFeePerGas: string, password: string): string =
let response = self.controller.setPubKey(ensUsername, address, gas, gasPrice, maxPriorityFeePerGas, maxFeePerGas, password)
if(response.len == 0):
info "expected response is empty", methodName="setPubKey"
return
let responseObj = response.parseJson
if (responseObj.kind != JObject):
info "expected response is not a json object", methodName="setPubKey"
return
var success: bool
if(not responseObj.getProp("success", success) or not success):
info "remote call is not executed with success", methodName="setPubKey"
return
var respResult: string
if(responseObj.getProp("result", respResult)):
self.view.model().addItem(Item(ensUsername: ensUsername, isPending: true))
self.view.emitTransactionWasSentSignal(respResult)
return response
method releaseEnsEstimate*(self: Module, ensUsername: string, address: string): int =
return self.controller.releaseEnsEstimate(ensUsername, address)
method release*(self: Module, ensUsername: string, address: string, gas: string, gasPrice: string, password: string): string =
let response = self.controller.release(ensUsername, address, gas, gasPrice, password)
if(response.len == 0):
info "expected response is empty", methodName="release"
return
let responseObj = response.parseJson
if(responseObj.kind != JObject):
info "expected response is not a json object", methodName="release"
return
var success: bool
if(not responseObj.getProp("success", success) or not success):
info "remote call is not executed with success", methodName="release"
return
var result: string
if(responseObj.getProp("result", result)):
self.controller.setPreferredName("")
self.view.model().removeItemByEnsUsername(ensUsername)
self.view.emitTransactionWasSentSignal(result)
return response
proc formatUsername(self: Module, ensUsername: string, isStatus: bool): string =
result = ensUsername
if isStatus:
result = ensUsername & ens_utils.STATUS_DOMAIN
method connectOwnedUsername*(self: Module, ensUsername: string, isStatus: bool) =
var ensUsername = self.formatUsername(ensUsername, isStatus)
if(not self.controller.saveNewEnsUsername(ensUsername)):
info "an error occurred saving ens username", methodName="connectOwnedUsername"
return
self.controller.setPreferredName(ensUsername)
self.view.model().addItem(Item(ensUsername: ensUsername, isPending: false))
method ensTransactionConfirmed*(self: Module, trxType: string, ensUsername: string, transactionHash: string) =
if(not self.controller.saveNewEnsUsername(ensUsername)):
info "an error occurred saving ens username", methodName="ensTransactionConfirmed"
return
if(self.view.model().containsEnsUsername(ensUsername)):
self.view.model().updatePendingStatus(ensUsername, false)
else:
self.view.model().addItem(Item(ensUsername: ensUsername, isPending: false))
self.view.emitTransactionCompletedSignal(true, transactionHash, ensUsername, trxType, "")
method ensTransactionReverted*(self: Module, trxType: string, ensUsername: string, transactionHash: string,
revertReason: string) =
self.view.model().removeItemByEnsUsername(ensUsername)
self.view.emitTransactionCompletedSignal(false, transactionHash, ensUsername, trxType, revertReason)
method getEnsRegisteredAddress*(self: Module): string =
return self.controller.getEnsRegisteredAddress()
method registerEnsGasEstimate*(self: Module, ensUsername: string, address: string): int =
return self.controller.registerEnsGasEstimate(ensUsername, address)
method registerEns*(self: Module, ensUsername: string, address: string, gas: string, gasPrice: string,
maxPriorityFeePerGas: string, maxFeePerGas: string, password: string): string =
let response = self.controller.registerEns(ensUsername, address, gas, gasPrice, maxPriorityFeePerGas, maxFeePerGas, password)
let responseObj = response.parseJson
if (responseObj.kind != JObject):
info "expected response is not a json object", methodName="registerEns"
return
var respResult: string
if(responseObj.getProp("result", respResult)):
self.view.model().addItem(Item(ensUsername: ensUsername, isPending: true))
self.view.emitTransactionWasSentSignal(respResult)
return response
method getSNTBalance*(self: Module): string =
return self.controller.getSNTBalance()
method getWalletDefaultAddress*(self: Module): string =
return self.controller.getWalletDefaultAddress()
method getCurrentCurrency*(self: Module): string =
return self.controller.getCurrentCurrency()
method getFiatValue*(self: Module, cryptoBalance: string, cryptoSymbol: string, fiatSymbol: string): string =
if (cryptoBalance == "" or cryptoSymbol == "" or fiatSymbol == ""):
return "0.00"
let price = self.controller.getPrice(cryptoSymbol, fiatSymbol)
let value = parseFloat(cryptoBalance) * price
return fmt"{value:.2f}"
method getGasEthValue*(self: Module, gweiValue: string, gasLimit: string): string {.slot.} =
var gasLimitInt:int
if(gasLimit.parseInt(gasLimitInt) == 0):
info "an error occurred parsing gas limit", methodName="getGasEthValue"
return ""
# The following check prevents app crash, cause we're trying to promote
# gasLimitInt to unsigned 256 int, and this number must be a positive number,
# because of overflow.
var gwei = gweiValue.parseFloat()
if (gwei < 0):
gwei = 0
if (gasLimitInt < 0):
gasLimitInt = 0
let weiValue = service_conversion.gwei2Wei(gwei) * gasLimitInt.u256
let ethValue = service_conversion.wei2Eth(weiValue)
return fmt"{ethValue}"
method getStatusToken*(self: Module): string =
return self.controller.getStatusToken()
method setPrefferedEnsUsername*(self: Module, ensUsername: string) =
self.controller.setPreferredName(ensUsername)

View File

@ -0,0 +1,13 @@
import NimQml
method delete*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method load*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method isLoaded*(self: AccessInterface): bool {.base.} =
raise newException(ValueError, "No implementation available")
method getModuleAsVariant*(self: AccessInterface): QVariant {.base.} =
raise newException(ValueError, "No implementation available")

View File

@ -0,0 +1,5 @@
type
AccessInterface* {.pure inheritable.} = ref object of RootObj
# Since nim doesn't support using concepts in second level nested types we
# define delegate interfaces within access interface.

View File

@ -0,0 +1,17 @@
method ensUsernameAvailabilityChecked*(self: AccessInterface, availabilityStatus: string) {.base.} =
raise newException(ValueError, "No implementation available")
method onDetailsForEnsUsername*(self: AccessInterface, ensUsername: string, address: string, pubkey: string,
isStatus: bool, expirationTime: int) {.base.} =
raise newException(ValueError, "No implementation available")
method gasPriceFetched*(self: AccessInterface, gasPrice: string) {.base.} =
raise newException(ValueError, "No implementation available")
method ensTransactionConfirmed*(self: AccessInterface, trxType: string, ensUsername: string, transactionHash: string)
{.base.} =
raise newException(ValueError, "No implementation available")
method ensTransactionReverted*(self: AccessInterface, trxType: string, ensUsername: string, transactionHash: string,
revertReason: string) {.base.} =
raise newException(ValueError, "No implementation available")

View File

@ -0,0 +1,63 @@
method viewDidLoad*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method checkEnsUsernameAvailability*(self: AccessInterface, desiredEnsUsername: string, statusDomain: bool) {.base.} =
raise newException(ValueError, "No implementation available")
method numOfPendingEnsUsernames*(self: AccessInterface): int {.base.} =
raise newException(ValueError, "No implementation available")
method fetchDetailsForEnsUsername*(self: AccessInterface, ensUsername: string) {.base.} =
raise newException(ValueError, "No implementation available")
method fetchGasPrice*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method setPubKeyGasEstimate*(self: AccessInterface, ensUsername: string, address: string): int {.base.} =
raise newException(ValueError, "No implementation available")
method setPubKey*(self: AccessInterface, ensUsername: string, address: string, gas: string, gasPrice: string,
maxPriorityFeePerGas: string, maxFeePerGas: string, password: string): string {.base.} =
raise newException(ValueError, "No implementation available")
method releaseEnsEstimate*(self: AccessInterface, ensUsername: string, address: string): int {.base.} =
raise newException(ValueError, "No implementation available")
method release*(self: AccessInterface, ensUsername: string, address: string, gas: string, gasPrice: string,
password: string): string {.base.} =
raise newException(ValueError, "No implementation available")
method connectOwnedUsername*(self: AccessInterface, ensUsername: string, isStatus: bool) {.base.} =
raise newException(ValueError, "No implementation available")
method getEnsRegisteredAddress*(self: AccessInterface): string {.base.} =
raise newException(ValueError, "No implementation available")
method registerEnsGasEstimate*(self: AccessInterface, ensUsername: string, address: string): int {.base.} =
raise newException(ValueError, "No implementation available")
method registerEns*(self: AccessInterface, ensUsername: string, address: string, gas: string, gasPrice: string,
maxPriorityFeePerGas: string, maxFeePerGas: string, password: string): string {.base.} =
raise newException(ValueError, "No implementation available")
method getSNTBalance*(self: AccessInterface): string {.base.} =
raise newException(ValueError, "No implementation available")
method getWalletDefaultAddress*(self: AccessInterface): string {.base.} =
raise newException(ValueError, "No implementation available")
method getCurrentCurrency*(self: AccessInterface): string {.base.} =
raise newException(ValueError, "No implementation available")
method getFiatValue*(self: AccessInterface, cryptoBalance: string, cryptoSymbol: string, fiatSymbol: string): string
{.base.} =
raise newException(ValueError, "No implementation available")
method getGasEthValue*(self: AccessInterface, gweiValue: string, gasLimit: string): string {.base.} =
raise newException(ValueError, "No implementation available")
method getStatusToken*(self: AccessInterface): string {.base.} =
raise newException(ValueError, "No implementation available")
method setPrefferedEnsUsername*(self: AccessInterface, ensUsername: string) {.base.} =
raise newException(ValueError, "No implementation available")

View File

@ -0,0 +1,149 @@
import NimQml
import io_interface
import model
from ../../../../../app_service/service/ens/utils import ENS_REGISTRY
QtObject:
type
View* = ref object of QObject
delegate: io_interface.AccessInterface
model: Model
modelVariant: QVariant
etherscanLink: string
signingPhrase: string
gasPrice: string
proc delete*(self: View) =
self.model.delete
self.modelVariant.delete
self.QObject.delete
proc newView*(delegate: io_interface.AccessInterface): View =
new(result, delete)
result.QObject.setup
result.model = newModel()
result.modelVariant = newQVariant(result.model)
result.delegate = delegate
result.gasPrice = "0"
proc load*(self: View, link: string, signingPhrase: string) =
self.etherscanLink = link
self.signingPhrase = ""
self.delegate.viewDidLoad()
proc model*(self: View): Model =
return self.model
proc modelChanged*(self: View) {.signal.}
proc getModel(self: View): QVariant {.slot.} =
return self.modelVariant
QtProperty[QVariant] model:
read = getModel
notify = modelChanged
proc getEnsRegistry(self: View): string {.slot.} =
return ENS_REGISTRY
proc usernameAvailabilityChecked(self: View, availabilityStatus: string) {.signal.}
proc sendUsernameAvailabilityCheckedSignal*(self: View, availabilityStatus: string) =
self.usernameAvailabilityChecked(availabilityStatus)
proc checkEnsUsernameAvailability*(self: View, desiredEnsUsername: string, statusDomain: bool) {.slot.} =
self.delegate.checkEnsUsernameAvailability(desiredEnsUsername, statusDomain)
proc numOfPendingEnsUsernames*(self: View): int {.slot.} =
return self.delegate.numOfPendingEnsUsernames()
proc loading(self: View, isLoading: bool) {.signal.}
proc detailsObtained(self: View, ensName: string, address: string, pubkey: string, isStatus: bool, expirationTime: int) {.signal.}
proc fetchDetailsForEnsUsername*(self: View, ensUsername: string) {.slot.} =
self.loading(true)
self.delegate.fetchDetailsForEnsUsername(ensUsername)
proc setDetailsForEnsUsername*(self: View, ensUsername: string, address: string, pubkey: string, isStatus: bool,
expirationTime: int) =
self.loading(false)
self.detailsObtained(ensUsername, address, pubkey, isStatus, expirationTime)
proc fetchGasPrice*(self: View) {.slot.} =
self.delegate.fetchGasPrice()
proc transactionWasSent(self: View, txResult: string) {.signal.}
proc emitTransactionWasSentSignal*(self: View, txResult: string) =
self.transactionWasSent(txResult)
proc setPubKeyGasEstimate*(self: View, ensUsername: string, address: string): int {.slot.} =
return self.delegate.setPubKeyGasEstimate(ensUsername, address)
proc setPubKey*(self: View, ensUsername: string, address: string, gas: string, gasPrice: string,
maxPriorityFeePerGas: string, maxFeePerGas: string, password: string): string {.slot.} =
return self.delegate.setPubKey(ensUsername, address, gas, gasPrice, maxPriorityFeePerGas, maxFeePerGas, password)
proc getEtherscanLink*(self: View): string {.slot.} =
return self.etherscanLink
proc getSigningPhrase*(self: View): string {.slot.} =
return self.signingPhrase
proc gasPriceChanged(self: View) {.signal.}
proc getGasPrice(self: View): string {.slot.} =
return self.gasPrice
QtProperty[string] gasPrice:
read = getGasPrice
notify = gasPriceChanged
proc setGasPrice*(self: View, gasPrice: string) = # this is not a slot
self.gasPrice = gasPrice
self.gasPriceChanged()
proc usernameConfirmed(self: View, username: string) {.signal.}
proc emitUsernameConfirmedSignal*(self: View, ensUsername: string) =
self.usernameConfirmed(ensUsername)
proc transactionCompleted(self: View, success: bool, txHash: string, username: string, trxType: string,
revertReason: string) {.signal.}
proc emitTransactionCompletedSignal*(self: View, success: bool, txHash: string, username: string, trxType: string,
revertReason: string) =
self.transactionCompleted(success, txHash, username, trxType, revertReason)
proc releaseEnsEstimate*(self: View, ensUsername: string, address: string): int {.slot.} =
return self.delegate.releaseEnsEstimate(ensUsername, address)
proc release*(self: View, ensUsername: string, address: string, gas: string, gasPrice: string, password: string):
string {.slot.} =
return self.delegate.release(ensUsername, address, gas, gasPrice, password)
proc connectOwnedUsername*(self: View, ensUsername: string, isStatus: bool) {.slot.} =
self.delegate.connectOwnedUsername(ensUsername, isStatus)
proc getEnsRegisteredAddress*(self: View): string {.slot.} =
return self.delegate.getEnsRegisteredAddress()
proc registerEnsGasEstimate*(self: View, ensUsername: string, address: string): int {.slot.} =
return self.delegate.registerEnsGasEstimate(ensUsername, address)
proc registerEns*(self: View, ensUsername: string, address: string, gas: string, gasPrice: string,
maxPriorityFeePerGas: string, maxFeePerGas: string, password: string): string {.slot.} =
return self.delegate.registerEns(ensUsername, address, gas, gasPrice, maxPriorityFeePerGas, maxFeePerGas, password)
proc getSNTBalance*(self: View): string {.slot.} =
return self.delegate.getSNTBalance()
proc getWalletDefaultAddress*(self: View): string {.slot.} =
return self.delegate.getWalletDefaultAddress()
proc getCurrentCurrency*(self: View): string {.slot.} =
return self.delegate.getCurrentCurrency()
proc getFiatValue*(self: View, cryptoBalance: string, cryptoSymbol: string, fiatSymbol: string): string {.slot.} =
return self.delegate.getFiatValue(cryptoBalance, cryptoSymbol, fiatSymbol)
proc getGasEthValue*(self: View, gweiValue: string, gasLimit: string): string {.slot.} =
return self.delegate.getGasEthValue(gweiValue, gasLimit)
proc getStatusToken*(self: View): string {.slot.} =
return self.delegate.getStatusToken()
proc setPrefferedEnsUsername*(self: View, ensUsername: string) {.slot.} =
self.delegate.setPrefferedEnsUsername(ensUsername)

View File

@ -74,6 +74,13 @@ method notificationsModuleDidLoad*(self: AccessInterface) {.base.} =
method getNotificationsModule*(self: AccessInterface): QVariant {.base.} =
raise newException(ValueError, "No implementation available")
method ensUsernamesModuleDidLoad*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method getEnsUsernamesModule*(self: AccessInterface): QVariant {.base.} =
raise newException(ValueError, "No implementation available")
type
## Abstract class (concept) which must be implemented by object/s used in this
## module.

View File

@ -13,6 +13,8 @@ import ../../../../app_service/service/node_configuration/service_interface as n
import ../../../../app_service/service/devices/service as devices_service
import ../../../../app_service/service/mailservers/service as mailservers_service
import ../../../../app_service/service/chat/service as chat_service
import ../../../../app_service/service/ens/service as ens_service
import ../../../../app_service/service/wallet_account/service_interface as wallet_account_service
import ./profile/module as profile_module
import ./contacts/module as contacts_module
@ -23,6 +25,7 @@ import ./advanced/module as advanced_module
import ./devices/module as devices_module
import ./sync/module as sync_module
import ./notifications/module as notifications_module
import ./ens_usernames/module as ens_usernames_module
export io_interface
@ -43,6 +46,7 @@ type
devicesModule: devices_module.AccessInterface
syncModule: sync_module.AccessInterface
notificationsModule: notifications_module.AccessInterface
ensUsernamesModule: ens_usernames_module.AccessInterface
proc newModule*[T](delegate: T,
events: EventEmitter,
@ -56,7 +60,9 @@ proc newModule*[T](delegate: T,
nodeConfigurationService: node_configuration_service.ServiceInterface,
devicesService: devices_service.Service,
mailserversService: mailservers_service.Service,
chatService: chat_service.Service
chatService: chat_service.Service,
ensService: ens_service.Service,
walletAccountService: wallet_account_service.ServiceInterface
):
Module[T] =
result = Module[T]()
@ -75,6 +81,8 @@ proc newModule*[T](delegate: T,
result.devicesModule = devices_module.newModule(result, events, settingsService, devicesService)
result.syncModule = sync_module.newModule(result, events, settingsService, mailserversService)
result.notificationsModule = notifications_module.newModule(result, events, chatService)
result.ensUsernamesModule = ens_usernames_module.newModule(result, events, settingsService, ensService,
walletAccountService)
singletonInstance.engine.setRootContextProperty("profileSectionModule", result.viewVariant)
@ -103,6 +111,7 @@ method load*[T](self: Module[T]) =
self.devicesModule.load()
self.syncModule.load()
self.notificationsModule.load()
self.ensUsernamesModule.load()
method isLoaded*[T](self: Module[T]): bool =
return self.moduleLoaded
@ -135,6 +144,9 @@ proc checkIfModuleDidLoad[T](self: Module[T]) =
if(not self.notificationsModule.isLoaded()):
return
if(not self.ensUsernamesModule.isLoaded()):
return
self.moduleLoaded = true
self.delegate.profileSectionDidLoad()
@ -191,3 +203,9 @@ method notificationsModuleDidLoad*[T](self: Module[T]) =
method getNotificationsModule*[T](self: Module[T]): QVariant =
self.notificationsModule.getModuleAsVariant()
method ensUsernamesModuleDidLoad*[T](self: Module[T]) =
self.checkIfModuleDidLoad()
method getEnsUsernamesModule*[T](self: Module[T]): QVariant =
self.ensUsernamesModule.getModuleAsVariant()

View File

@ -60,3 +60,8 @@ QtObject:
return self.delegate.getPrivacyModule()
QtProperty[QVariant] privacyModule:
read = getPrivacyModule
proc getEnsUsernamesModule(self: View): QVariant {.slot.} =
return self.delegate.getEnsUsernamesModule()
QtProperty[QVariant] ensUsernamesModule:
read = getEnsUsernamesModule

View File

@ -0,0 +1,60 @@
import strutils, strformat, stint, chronicles
from web3 import Address, fromHex
proc parseAddress*(strAddress: string): Address =
fromHex(Address, strAddress)
proc isAddress*(strAddress: string): bool =
try:
discard parseAddress(strAddress)
except:
return false
return true
proc toStUInt*[bits: static[int]](flt: float, T: typedesc[StUint[bits]]): T =
var stringValue = fmt"{flt:<.0f}"
stringValue.removeSuffix('.')
if (flt >= 0):
result = parse($stringValue, StUint[bits])
else:
result = parse("0", StUint[bits])
proc toUInt256*(flt: float): UInt256 =
toStUInt(flt, StUInt[256])
proc toUInt64*(flt: float): StUInt[64] =
toStUInt(flt, StUInt[64])
proc eth2Wei*(eth: float, decimals: int = 18): UInt256 =
let weiValue = eth * parseFloat(alignLeft("1", decimals + 1, '0'))
weiValue.toUInt256
proc gwei2Wei*(gwei: float): UInt256 =
eth2Wei(gwei, 9)
proc wei2Eth*(input: Stuint[256], decimals: int = 18): string =
var one_eth = u256(10).pow(decimals) # fromHex(Stuint[256], "DE0B6B3A7640000")
var (eth, remainder) = divmod(input, one_eth)
let leading_zeros = "0".repeat(($one_eth).len - ($remainder).len - 1)
fmt"{eth}.{leading_zeros}{remainder}"
proc wei2Eth*(input: string, decimals: int): string =
try:
var input256: Stuint[256]
if input.contains("e+"): # we have a js string BN, ie 1e+21
let
inputSplit = input.split("e+")
whole = inputSplit[0].u256
remainder = u256(10).pow(inputSplit[1].parseInt)
input256 = whole * remainder
else:
input256 = input.u256
result = wei2Eth(input256, decimals)
except Exception as e:
error "Error parsing this wei value", input, msg=e.msg
result = "0"
proc wei2Gwei*(input: string): string =
result = wei2Eth(input, 9)

View File

@ -0,0 +1,81 @@
#################################################
# Async check for ens username availability
#################################################
type
CheckEnsAvailabilityTaskArg = ref object of QObjectTaskArg
ensUsername*: string
isStatus*: bool
myPublicKey*: string
myWalletAddress*: string
const checkEnsAvailabilityTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
let arg = decode[CheckEnsAvailabilityTaskArg](argEncoded)
var desiredEnsUsername = arg.ensUsername & (if(arg.isStatus): ens_utils.STATUS_DOMAIN else: "")
var availability = ""
let ownerAddr = ens_utils.owner(desiredEnsUsername)
if ownerAddr == "" and arg.isStatus:
availability = ENS_AVAILABILITY_STATUS_AVAILABLE
else:
let ensPubkey = ens_utils.pubkey(arg.ensUsername)
if ownerAddr != "":
if ensPubkey == "" and ownerAddr == arg.myWalletAddress:
availability = ENS_AVAILABILITY_STATUS_OWNED # "Continuing will connect this username with your chat key."
elif ensPubkey == arg.myPublicKey:
availability = ENS_AVAILABILITY_STATUS_CONNECTED
elif ownerAddr == arg.myWalletAddress:
availability = ENS_AVAILABILITY_STATUS_CONNECTED_DIFFERENT_KEY # "Continuing will require a transaction to connect the username with your current chat key.",
else:
availability = ENS_AVAILABILITY_STATUS_TAKEN
else:
availability = ENS_AVAILABILITY_STATUS_TAKEN
let responseJson = %*{
"availability": availability
}
arg.finish(responseJson)
#################################################
# Async load ens username details
#################################################
type
EnsUsernamDetailsTaskArg = ref object of QObjectTaskArg
ensUsername*: string
isStatus*: bool
toAddress*: Address
data: string
const ensUsernameDetailsTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
let arg = decode[EnsUsernamDetailsTaskArg](argEncoded)
let address = ens_utils.address(arg.ensUsername)
let pubkey = ens_utils.pubkey(arg.ensUsername)
var expirationTime = 0
if arg.isStatus:
expirationTime = ens_utils.getExpirationTime(arg.toAddress, arg.data)
let responseJson = %* {
"ensUsername": arg.ensUsername,
"address": address,
"pubkey": pubkey,
"isStatus": arg.isStatus,
"expirationTime": expirationTime
}
arg.finish(responseJson)
#################################################
# Async fetch gas price
#################################################
const fetchGasPriceTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
let arg = decode[QObjectTaskArg](argEncoded)
let response = status_eth.getGasPrice()
let responseJson = %* {
"gasPrice": response.result.getStr
}
arg.finish(responseJson)

View File

@ -0,0 +1,486 @@
import NimQml, Tables, sets, json, sequtils, strutils, strformat, chronicles
import web3/conversions
import web3/[conversions, ethtypes], stint
import web3/ethtypes
import ../../../app/core/eventemitter
import ../../../app/core/tasks/[qt, threadpool]
import status/statusgo_backend_new/eth as status_eth
import ../../common/conversion as common_conversion
import utils as ens_utils
import ../settings/service_interface as settings_service
import ../wallet_account/service_interface as wallet_account_service
import ../transaction/service as transaction_service
import ../eth/service_interface as eth_service
import ../network/service_interface as network_service
import ../token/service as token_service
logScope:
topics = "ens-service"
const ENS_AVAILABILITY_STATUS_ALREADY_CONNECTED = "already-connected"
const ENS_AVAILABILITY_STATUS_AVAILABLE = "available"
const ENS_AVAILABILITY_STATUS_OWNED = "owned"
const ENS_AVAILABILITY_STATUS_CONNECTED = "connected"
const ENS_AVAILABILITY_STATUS_CONNECTED_DIFFERENT_KEY = "connected-different-key"
const ENS_AVAILABILITY_STATUS_TAKEN = "taken"
include ../../common/json_utils
include async_tasks
type
EnsUsernameAvailabilityArgs* = ref object of Args
availabilityStatus*: string
EnsUsernameDetailsArgs* = ref object of Args
ensUsername*: string
address*: string
pubkey*: string
isStatus*: bool
expirationTime*: int
GasPriceArgs* = ref object of Args
gasPrice*: string
EnsTransactionArgs* = ref object of Args
transactionHash*: string
ensUsername*: string
transactionType*: string
revertReason*: string
# Signals which may be emitted by this service:
const SIGNAL_ENS_USERNAME_AVAILABILITY_CHECKED* = "ensUsernameAvailabilityChecked"
const SIGNAL_ENS_USERNAME_DETAILS_FETCHED* = "ensUsernameDetailsFetched"
const SIGNAL_GAS_PRICE_FETCHED* = "gasPriceFetched"
const SIGNAL_ENS_TRANSACTION_CONFIRMED* = "ensTransactionConfirmed"
const SIGNAL_ENS_TRANSACTION_REVERTED* = "ensTransactionReverted"
QtObject:
type
Service* = ref object of QObject
events: EventEmitter
threadpool: ThreadPool
pendingEnsUsernames*: HashSet[string]
settingsService: settings_service.ServiceInterface
walletAccountService: wallet_account_service.ServiceInterface
transactionService: transaction_service.Service
ethService: eth_service.ServiceInterface
networkService: network_service.ServiceInterface
tokenService: token_service.Service
proc delete*(self: Service) =
self.QObject.delete
proc newService*(
events: EventEmitter,
threadpool: ThreadPool,
settingsService: settings_service.ServiceInterface,
walletAccountService: wallet_account_service.ServiceInterface,
transactionService: transaction_service.Service,
ethService: eth_service.ServiceInterface,
networkService: network_service.ServiceInterface,
tokenService: token_service.Service
): Service =
new(result, delete)
result.QObject.setup
result.events = events
result.threadpool = threadpool
result.settingsService = settingsService
result.walletAccountService = walletAccountService
result.transactionService = transactionService
result.ethService = ethService
result.networkService = networkService
result.tokenService = tokenService
proc confirmTransaction(self: Service, trxType: string, ensUsername: string, transactionHash: string) =
self.pendingEnsUsernames.excl(ensUsername)
let data = EnsTransactionArgs(transactionHash: transactionHash, ensUsername: ensUsername, transactionType: $trxType)
self.events.emit(SIGNAL_ENS_TRANSACTION_CONFIRMED, data)
proc revertTransaction(self: Service, trxType: string, ensUsername: string, transactionHash: string,
revertReason: string) =
self.pendingEnsUsernames.excl(ensUsername)
let data = EnsTransactionArgs(transactionHash: transactionHash, ensUsername: ensUsername, transactionType: $trxType,
revertReason: revertReason)
self.events.emit(SIGNAL_ENS_TRANSACTION_REVERTED, data)
proc doConnect(self: Service) =
self.events.on(PendingTransactionTypeDto.RegisterENS.event) do(e: Args):
var receivedData = TransactionMinedArgs(e)
if receivedData.success:
self.confirmTransaction($PendingTransactionTypeDto.RegisterENS, receivedData.data, receivedData.transactionHash)
else:
self.revertTransaction($PendingTransactionTypeDto.RegisterENS, receivedData.data, receivedData.transactionHash,
receivedData.revertReason)
self.events.on(PendingTransactionTypeDto.SetPubKey.event) do(e: Args):
var receivedData = TransactionMinedArgs(e)
if receivedData.success:
self.confirmTransaction($PendingTransactionTypeDto.SetPubKey, receivedData.data, receivedData.transactionHash)
else:
self.revertTransaction($PendingTransactionTypeDto.SetPubKey, receivedData.data, receivedData.transactionHash,
receivedData.revertReason)
proc init*(self: Service) =
self.doConnect()
# Response of `transactionService.getPendingTransactions()` should be appropriate DTO, that's not added at the moment
# but once we add it, need to update this block here, since we won't need to parse json manually here.
let pendingTransactions = self.transactionService.getPendingTransactions()
var pendingStickerPacks = initHashSet[int]()
if (pendingTransactions.len > 0):
for trx in pendingTransactions.parseJson{"result"}.getElems():
let transactionType = trx["type"].getStr
if transactionType == $PendingTransactionTypeDto.RegisterENS or
transactionType == $PendingTransactionTypeDto.SetPubKey:
self.pendingEnsUsernames.incl trx["additionalData"].getStr
proc getMyPendingEnsUsernames*(self: Service): seq[string] =
for i in self.pendingEnsUsernames.items:
result.add(i)
proc getAllMyEnsUsernames*(self: Service, includePending: bool): seq[string] =
result = self.settingsService.getEnsUsernames()
if(includePending):
result.add(self.getMyPendingEnsUsernames())
proc onEnsUsernameAvailabilityChecked*(self: Service, response: string) {.slot.} =
let responseObj = response.parseJson
if (responseObj.kind != JObject):
info "expected response is not a json object", methodName="onEnsUsernameAvailabilityChecked"
# notify view, this is important
self.events.emit(SIGNAL_ENS_USERNAME_AVAILABILITY_CHECKED, EnsUsernameAvailabilityArgs())
return
var availablilityStatus: string
discard responseObj.getProp("availability", availablilityStatus)
let data = EnsUsernameAvailabilityArgs(availabilityStatus: availablilityStatus)
self.events.emit(SIGNAL_ENS_USERNAME_AVAILABILITY_CHECKED, data)
proc formatUsername(self: Service, username: string, isStatus: bool): string =
result = username
if isStatus:
result = result & ens_utils.STATUS_DOMAIN
proc checkEnsUsernameAvailability*(self: Service, ensUsername: string, isStatus: bool) =
let registeredEnsUsernames = self.getAllMyEnsUsernames(true)
var desiredEnsUsername = self.formatUsername(ensUsername, isStatus)
var availability = ""
if registeredEnsUsernames.filter(proc(x: string):bool = x == desiredEnsUsername).len > 0:
let data = EnsUsernameAvailabilityArgs(availabilityStatus: ENS_AVAILABILITY_STATUS_ALREADY_CONNECTED)
self.events.emit(SIGNAL_ENS_USERNAME_AVAILABILITY_CHECKED, data)
else:
let arg = CheckEnsAvailabilityTaskArg(
tptr: cast[ByteAddress](checkEnsAvailabilityTask),
vptr: cast[ByteAddress](self.vptr),
slot: "onEnsUsernameAvailabilityChecked",
ensUsername: ensUsername,
isStatus: isStatus,
myPublicKey: self.settingsService.getPublicKey(),
myWalletAddress: self.walletAccountService.getWalletAccount(0).address
)
self.threadpool.start(arg)
proc onEnsUsernameDetailsFetched*(self: Service, response: string) {.slot.} =
let responseObj = response.parseJson
if (responseObj.kind != JObject):
info "expected response is not a json object", methodName="onEnsUsernameDetailsFetched"
# notify view, this is important
self.events.emit(SIGNAL_ENS_USERNAME_DETAILS_FETCHED, EnsUsernameDetailsArgs())
return
var data = EnsUsernameDetailsArgs()
discard responseObj.getProp("ensUsername", data.ensUsername)
discard responseObj.getProp("address", data.address)
discard responseObj.getProp("pubkey", data.pubkey)
discard responseObj.getProp("isStatus", data.isStatus)
discard responseObj.getProp("expirationTime", data.expirationTime)
self.events.emit(SIGNAL_ENS_USERNAME_DETAILS_FETCHED, data)
proc sntSymbol(networkType: NetworkType): string =
if networkType == NetworkType.Mainnet:
return "SNT"
else:
return "STT"
proc getCurrentNetworkContractForName(self: Service, name: string): ContractDto =
let networkType = self.settingsService.getCurrentNetwork().toNetworkType()
let networkDto = self.networkService.getNetwork(networkType)
return self.ethService.findContract(networkDto.chainId, name)
proc getCurrentNetworkErc20ContractForSymbol(self: Service, symbol: string = ""): Erc20ContractDto =
let networkType = self.settingsService.getCurrentNetwork().toNetworkType()
let networkDto = self.networkService.getNetwork(networkType)
return self.ethService.findErc20Contract(networkDto.chainId, if symbol.len > 0: symbol else: networkType.sntSymbol())
proc fetchDetailsForEnsUsername*(self: Service, ensUsername: string) =
var contractDto = self.getCurrentNetworkContractForName("ens-usernames")
var data: string
var isStatus = false
if ensUsername.endsWith(ens_utils.STATUS_DOMAIN):
let onlyUsername = ensUsername.replace(ens_utils.STATUS_DOMAIN, "")
let label = fromHex(FixedBytes[32], label(onlyUsername))
let expTime = ExpirationTime(label: label)
isStatus = true
data = contractDto.methods["getExpirationTime"].encodeAbi(expTime)
let arg = EnsUsernamDetailsTaskArg(
tptr: cast[ByteAddress](ensUsernameDetailsTask),
vptr: cast[ByteAddress](self.vptr),
slot: "onEnsUsernameDetailsFetched",
ensUsername: ensUsername,
isStatus: isStatus,
toAddress: contractDto.address,
data: data
)
self.threadpool.start(arg)
proc onGasPriceFetched*(self: Service, response: string) {.slot.} =
let responseObj = response.parseJson
if (responseObj.kind != JObject):
info "expected response is not a json object", methodName="onGasPriceFetched"
# notify view, this is important
self.events.emit(SIGNAL_GAS_PRICE_FETCHED, GasPriceArgs(gasPrice: "0"))
return
var gasPriceHex: string
if(not responseObj.getProp("gasPrice", gasPriceHex)):
info "expected response doesn't contain gas price", methodName="onGasPriceFetched"
# notify view, this is important
self.events.emit(SIGNAL_GAS_PRICE_FETCHED, GasPriceArgs(gasPrice: "0"))
return
let gasPrice = $fromHex(Stuint[256], gasPriceHex)
let parsedGasPrice = parseFloat(wei2gwei(gasPrice))
var data = GasPriceArgs(gasPrice: fmt"{parsedGasPrice:.3f}")
self.events.emit(SIGNAL_GAS_PRICE_FETCHED, data)
proc fetchGasPrice*(self: Service) =
let arg = QObjectTaskArg(
tptr: cast[ByteAddress](fetchGasPriceTask),
vptr: cast[ByteAddress](self.vptr),
slot: "onGasPriceFetched"
)
self.threadpool.start(arg)
proc extractCoordinates(self: Service, pubkey: string):tuple[x: string, y:string] =
result = ("0x" & pubkey[4..67], "0x" & pubkey[68..131])
proc setPubKeyGasEstimate*(self: Service, ensUsername: string, address: string): int =
try:
let myPublicKey = self.settingsService.getPublicKey()
var hash = namehash(ensUsername)
hash.removePrefix("0x")
let label = fromHex(FixedBytes[32], "0x" & hash)
let coordinates = self.extractCoordinates(myPublicKey)
let x = fromHex(FixedBytes[32], coordinates.x)
let y = fromHex(FixedBytes[32], coordinates.y)
let contractDto = self.getCurrentNetworkContractForName("ens-resolver")
let setPubkey = SetPubkey(label: label, x: x, y: y)
let resolverAddress = resolver(hash)
var tx = buildTokenTransaction(parseAddress(address), parseAddress(resolverAddress), "", "")
var success = false
let response = contractDto.methods["setPubkey"].estimateGas(tx, setPubkey, success)
if(success):
result = fromHex[int](response)
else:
result = 80000
except RpcException as e:
result = 80000
error "error occurred", methodName="setPubKeyGasEstimate"
proc setPubKey*(self: Service, ensUsername: string, address: string, gas: string, gasPrice: string,
maxPriorityFeePerGas: string, maxFeePerGas: string, password: string): string =
try:
let eip1559Enabled = self.settingsService.isEIP1559Enabled()
let myPublicKey = self.settingsService.getPublicKey()
var hash = namehash(ensUsername)
hash.removePrefix("0x")
let label = fromHex(FixedBytes[32], "0x" & hash)
let coordinates = self.extractCoordinates(myPublicKey)
let x = fromHex(FixedBytes[32], coordinates.x)
let y = fromHex(FixedBytes[32], coordinates.y)
let contractDto = self.getCurrentNetworkContractForName("ens-resolver")
let setPubkey = SetPubkey(label: label, x: x, y: y)
let resolverAddress = resolver(hash)
var tx = buildTokenTransaction(parseAddress(address), parseAddress(resolverAddress), gas, gasPrice,
eip1559Enabled, maxPriorityFeePerGas, maxFeePerGas)
var success = false
let response = contractDto.methods["setPubkey"].send(tx, setPubkey, password, success)
result = $(%* { "result": %response, "success": %success })
if success:
self.transactionService.trackPendingTransaction(response, address, resolverAddress,
$PendingTransactionTypeDto.SetPubKey, ensUsername)
self.pendingEnsUsernames.incl(ensUsername)
except RpcException as e:
error "error occurred", methodName="setPubKey"
proc releaseEnsEstimate*(self: Service, ensUsername: string, address: string): int =
try:
let label = fromHex(FixedBytes[32], label(ensUsername))
let contractDto = self.getCurrentNetworkContractForName("ens-resolver")
let release = Release(label: label)
var tx = buildTokenTransaction(parseAddress(address), contractDto.address, "", "")
var success = false
let response = contractDto.methods["release"].estimateGas(tx, release, success)
if(success):
result = fromHex[int](response)
else:
result = 100000
except RpcException as e:
result = 100000
error "error occurred", methodName="releaseEnsEstimate"
proc release*(self: Service, ensUsername: string, address: string, gas: string, gasPrice: string, password: string):
string =
try:
let label = fromHex(FixedBytes[32], label(ensUsername))
let contractDto = self.getCurrentNetworkContractForName("ens-usernames")
let release = Release(label: label)
var tx = buildTokenTransaction(parseAddress(address), contractDto.address, "", "")
var success = false
let response = contractDto.methods["release"].send(tx, release, password, success)
result = $(%* { "result": %response, "success": %success })
if(success):
self.transactionService.trackPendingTransaction(response, address, $contractDto.address,
$PendingTransactionTypeDto.ReleaseENS, ensUsername)
self.pendingEnsUsernames.excl(ensUsername)
except RpcException as e:
error "error occurred", methodName="release"
proc getEnsRegisteredAddress*(self: Service): string =
let contractDto = self.getCurrentNetworkContractForName("ens-usernames")
if contractDto != nil:
return $contractDto.address
proc registerENSGasEstimate*(self: Service, ensUsername: string, address: string): int =
try:
let myPublicKey = self.settingsService.getPublicKey()
let coordinates = self.extractCoordinates(myPublicKey)
let x = fromHex(FixedBytes[32], coordinates.x)
let y = fromHex(FixedBytes[32], coordinates.y)
let contractDto = self.getCurrentNetworkContractForName("ens-usernames")
let sntContract = self.getCurrentNetworkErc20ContractForSymbol()
let price = getPrice(contractDto)
let label = fromHex(FixedBytes[32], label(ensUsername))
let register = Register(label: label, account: parseAddress(address), x: x, y: y)
let registerAbiEncoded = contractDto.methods["register"].encodeAbi(register)
let approveAndCallObj = ApproveAndCall[132](to: contractDto.address, value: price,
data: DynamicBytes[132].fromHex(registerAbiEncoded))
let approveAndCallAbiEncoded = sntContract.methods["approveAndCall"].encodeAbi(approveAndCallObj)
var tx = buildTokenTransaction(parseAddress(address), sntContract.address, "", "")
var success = false
let response = sntContract.methods["approveAndCall"].estimateGas(tx, approveAndCallObj, success)
if(success):
result = fromHex[int](response)
else:
result = 380000
except RpcException as e:
error "error occurred", methodName="registerENSGasEstimate"
proc registerEns*(self: Service, username: string, address: string, gas: string, gasPrice: string,
maxPriorityFeePerGas: string, maxFeePerGas: string, password: string): string =
try:
let myPublicKey = self.settingsService.getPublicKey()
let coordinates = self.extractCoordinates(myPublicKey)
let x = fromHex(FixedBytes[32], coordinates.x)
let y = fromHex(FixedBytes[32], coordinates.y)
let contractDto = self.getCurrentNetworkContractForName("ens-usernames")
let sntContract = self.getCurrentNetworkErc20ContractForSymbol()
let price = getPrice(contractDto)
let label = fromHex(FixedBytes[32], label(username))
let eip1559Enabled = self.settingsService.isEIP1559Enabled()
let register = Register(label: label, account: parseAddress(address), x: x, y: y)
let registerAbiEncoded = contractDto.methods["register"].encodeAbi(register)
let approveAndCallObj = ApproveAndCall[132](to: contractDto.address, value: price,
data: DynamicBytes[132].fromHex(registerAbiEncoded))
var tx = buildTokenTransaction(parseAddress(address), sntContract.address, gas, gasPrice, eip1559Enabled,
maxPriorityFeePerGas, maxFeePerGas)
var success = false
let response = sntContract.methods["approveAndCall"].send(tx, approveAndCallObj, password, success)
result = $(%* { "result": %response, "success": %success })
if success:
var ensUsername = self.formatUsername(username, true)
self.transactionService.trackPendingTransaction(response, address, $sntContract.address,
$PendingTransactionTypeDto.RegisterENS, ensUsername)
self.pendingEnsUsernames.incl(ensUsername)
except RpcException as e:
error "error occurred", methodName="registerEns"
proc getSNTBalance*(self: Service): string =
let address = self.walletAccountService.getWalletAccount(0).address
let sntContract = self.getCurrentNetworkErc20ContractForSymbol()
var postfixedAccount: string = address
postfixedAccount.removePrefix("0x")
let payload = %* [{
"to": $sntContract.address,
"from": address,
"data": fmt"0x70a08231000000000000000000000000{postfixedAccount}"
}, "latest"]
let response = status_eth.doEthCall(payload)
let balance = response.result.getStr
var decimals = 18
let allTokens = self.tokenService.getTokens()
for t in allTokens:
if(t.address == sntContract.address):
decimals = t.decimals
break
result = ens_utils.hex2Token(balance, decimals)
proc getStatusToken*(self: Service): string =
let sntContract = self.getCurrentNetworkErc20ContractForSymbol()
let jsonObj = %* {
"name": sntContract.name,
"symbol": sntContract.symbol,
"address": sntContract.address
}
return $jsonObj

View File

@ -1,14 +1,34 @@
import Tables, json, chronicles, strutils
import algorithm, strformat, sets, options
import algorithm, strformat, sets, options, sequtils
import chronicles, libp2p/[multihash, multicodec, cid]
import nimcrypto, stint
include ../../common/json_utils
import web3/conversions
import ../../common/conversion as common_conversion
import ../eth/dto/transaction as eth_transaction_dto
import ../eth/dto/coder as eth_coder_dto
import ../eth/dto/contract as eth_contract_dto
import status/statusgo_backend_new/eth as status_eth
logScope:
topics = "ens-utils"
const domain* = ".stateofus.eth"
include ../../common/json_utils
const STATUS_DOMAIN* = ".stateofus.eth"
const ENS_REGISTRY* = "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e"
const RESOLVER_SIGNATURE* = "0x0178b8bf"
const CONTENTHASH_SIGNATURE* = "0xbc1c58d1" # contenthash(bytes32)
const PUBKEY_SIGNATURE* = "0xc8690233" # pubkey(bytes32 node)
const ADDRESS_SIGNATURE* = "0x3b3b57de" # addr(bytes32 node)
const OWNER_SIGNATURE* = "0x02571be3" # owner(bytes32 node)
export STATUS_DOMAIN
export ENS_REGISTRY
export RESOLVER_SIGNATURE
export CONTENTHASH_SIGNATURE
export PUBKEY_SIGNATURE
export ADDRESS_SIGNATURE
export OWNER_SIGNATURE
type
ENSType* {.pure.} = enum
@ -21,9 +41,9 @@ proc addDomain(username: string): string =
if username.endsWith(".eth"):
return username
else:
return username & domain
return username & STATUS_DOMAIN
proc namehash(ensName:string): string =
proc namehash*(ensName:string): string =
let name = ensName.toLower()
var node:array[32, byte]
@ -38,20 +58,17 @@ proc namehash(ensName:string): string =
result = "0x" & node.toHex()
const registry* = "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e"
const resolver_signature = "0x0178b8bf"
proc resolver(usernameHash: string): string =
proc resolver*(usernameHash: string): string =
let payload = %* [{
"to": registry,
"to": ENS_REGISTRY,
"from": "0x0000000000000000000000000000000000000000",
"data": fmt"{resolver_signature}{userNameHash}"
"data": fmt"{RESOLVER_SIGNATURE}{userNameHash}"
}, "latest"]
var resolverAddr = status_eth.doEthCall(payload).result.getStr()
resolverAddr.removePrefix("0x000000000000000000000000")
result = "0x" & resolverAddr
const contenthash_signature = "0xbc1c58d1" # contenthash(bytes32)
proc contenthash(ensAddr: string): string =
var ensHash = namehash(ensAddr)
ensHash.removePrefix("0x")
@ -59,7 +76,7 @@ proc contenthash(ensAddr: string): string =
let payload = %* [{
"to": ensResolver,
"from": "0x0000000000000000000000000000000000000000",
"data": fmt"{contenthash_signature}{ensHash}"
"data": fmt"{CONTENTHASH_SIGNATURE}{ensHash}"
}, "latest"]
let bytesResponse = status_eth.doEthCall(payload).result.getStr()
@ -120,7 +137,6 @@ proc decodeENSContentHash*(value: string): tuple[ensType: ENSType, output: strin
return (ENSType.UNKNOWN, "")
const pubkey_signature = "0xc8690233" # pubkey(bytes32 node)
proc pubkey*(username: string): string =
var userNameHash = namehash(addDomain(username))
userNameHash.removePrefix("0x")
@ -128,7 +144,7 @@ proc pubkey*(username: string): string =
let payload = %* [{
"to": ensResolver,
"from": "0x0000000000000000000000000000000000000000",
"data": fmt"{pubkey_signature}{userNameHash}"
"data": fmt"{PUBKEY_SIGNATURE}{userNameHash}"
}, "latest"]
let response = status_eth.doEthCall(payload)
# TODO: error handling
@ -139,7 +155,6 @@ proc pubkey*(username: string): string =
pubkey.removePrefix("0x")
result = "0x04" & pubkey
const address_signature = "0x3b3b57de" # addr(bytes32 node)
proc address*(username: string): string =
var userNameHash = namehash(addDomain(username))
userNameHash.removePrefix("0x")
@ -147,7 +162,7 @@ proc address*(username: string): string =
let payload = %* [{
"to": ensResolver,
"from": "0x0000000000000000000000000000000000000000",
"data": fmt"{address_signature}{userNameHash}"
"data": fmt"{ADDRESS_SIGNATURE}{userNameHash}"
}, "latest"]
let response = status_eth.doEthCall(payload)
# TODO: error handling
@ -155,3 +170,86 @@ proc address*(username: string): string =
if address == "0x0000000000000000000000000000000000000000000000000000000000000000":
return ""
result = "0x" & address.substr(26)
proc owner*(username: string): string =
var userNameHash = namehash(addDomain(username))
userNameHash.removePrefix("0x")
let payload = %* [{
"to": ENS_REGISTRY,
"from": "0x0000000000000000000000000000000000000000",
"data": fmt"{OWNER_SIGNATURE}{userNameHash}"
}, "latest"]
let response = status_eth.doEthCall(payload)
# TODO: error handling
let ownerAddr = response.result.getStr()
if ownerAddr == "0x0000000000000000000000000000000000000000000000000000000000000000":
return ""
result = "0x" & ownerAddr.substr(26)
proc buildTransaction*(source: Address, value: Uint256, gas = "", gasPrice = "", isEIP1559Enabled = false,
maxPriorityFeePerGas = "", maxFeePerGas = "", data = ""): TransactionDataDto =
result = TransactionDataDto(
source: source,
value: value.some,
gas: (if gas.isEmptyOrWhitespace: Quantity.none else: Quantity(cast[uint64](parseFloat(gas).toUInt64)).some),
gasPrice: (if gasPrice.isEmptyOrWhitespace: int.none else: gwei2Wei(parseFloat(gasPrice)).truncate(int).some),
data: data
)
if isEIP1559Enabled:
result.txType = "0x02"
result.maxPriorityFeePerGas = if maxFeePerGas.isEmptyOrWhitespace: Uint256.none else: gwei2Wei(parseFloat(maxPriorityFeePerGas)).some
result.maxFeePerGas = (if maxFeePerGas.isEmptyOrWhitespace: Uint256.none else: gwei2Wei(parseFloat(maxFeePerGas)).some)
else:
result.txType = "0x00"
proc buildTokenTransaction*(source, contractAddress: Address, gas = "", gasPrice = "", isEIP1559Enabled = false,
maxPriorityFeePerGas = "", maxFeePerGas = ""): TransactionDataDto =
result = buildTransaction(source, 0.u256, gas, gasPrice, isEIP1559Enabled, maxPriorityFeePerGas, maxFeePerGas)
result.to = contractAddress.some
proc label*(username:string): string =
var node:array[32, byte] = keccak_256.digest(username.toLower()).data
result = "0x" & node.toHex()
proc getExpirationTime*(toAddress: Address, data: string): int =
try:
var tx = buildTransaction(parseAddress("0x0000000000000000000000000000000000000000"), 0.u256)
tx.to = toAddress.some
tx.data = data
let payload = %*[%tx, "latest"]
let response = status_eth.doEthCall(payload)
result = fromHex[int](response.result.getStr)
except RpcException as e:
error "Error obtaining expiration time", err=e.msg
proc getPrice*(ensUsernamesContract: ContractDto): Stuint[256] =
try:
let payload = %* [{
"to": $ensUsernamesContract.address,
"data": ensUsernamesContract.methods["getPrice"].encodeAbi()
}, "latest"]
let response = status_eth.doEthCall(payload)
if not response.error.isNil:
error "Error getting ens username price, ", errDescription=response.error.message
if response.result.getStr == "0x":
error "Error getting ens username price: 0x"
result = fromHex(Stuint[256], response.result.getStr)
except RpcException as e:
error "Error obtaining expiration time", err=e.msg
proc hex2Token*(input: string, decimals: int): string =
var value = fromHex(Stuint[256], input)
if decimals == 0:
return fmt"{value}"
var p = u256(10).pow(decimals)
var i = value.div(p)
var r = value.mod(p)
var leading_zeros = "0".repeat(decimals - ($r).len)
var d = fmt"{leading_zeros}{$r}"
result = $i
if(r > 0): result = fmt"{result}.{d}"

View File

@ -9,6 +9,8 @@ import ./method_dto
import status/statusgo_backend_new/eth as status_eth
export method_dto
include ../../../common/json_utils
type

View File

@ -2,14 +2,6 @@ import strutils, json
import web3/ethtypes, web3/conversions, options, stint
import ../utils
type
PendingTransactionTypeDto* {.pure.} = enum
RegisterENS = "RegisterENS",
SetPubKey = "SetPubKey",
ReleaseENS = "ReleaseENS",
BuyStickerPack = "BuyStickerPack"
WalletTransfer = "WalletTransfer"
type
TransactionDataDto* = object
source*: Address # the address the transaction is send from.

View File

@ -4,20 +4,9 @@ import web3/ethtypes, json_serialization, chronicles, tables
import status/statusgo_backend_new/eth
import utils
import ./dto/contract
import ./dto/method_dto
import ./dto/network
import ./dto/coder
import ./dto/edn_dto
import ./dto/transaction
import ./service_interface
export service_interface
export coder
export edn_dto
export contract
export method_dto
export transaction
logScope:
topics = "eth-service"
@ -34,12 +23,12 @@ proc newService*(): Service =
result.contracts = @[]
# Forward declaration
method initContracts(self: Service)
proc initContracts(self: Service)
method init*(self: Service) =
self.initContracts()
method initContracts(self: Service) =
proc initContracts(self: Service) =
self.contracts = @[
# Mainnet contracts
newErc20Contract("Status Network Token", Mainnet, parseAddress("0x744d70fdbe2ba4cf95131626614a1763df805b9e"), "SNT", 18, true),
@ -221,7 +210,7 @@ method initContracts(self: Service) =
newErc20Contract("Orchid", Mainnet, parseAddress("0x4575f41308EC1483f3d399aa9a2826d74Da13Deb"), "OXT", 18, false),
]
method allContracts(self: Service): seq[ContractDto] =
proc allContracts(self: Service): seq[ContractDto] =
result = self.contracts
method findByAddress*(self: Service, contracts: seq[Erc20ContractDto], address: Address): Erc20ContractDto =
@ -254,22 +243,3 @@ method allErc721ContractsByChainId*(self: Service, chainId: int): seq[Erc721Cont
method findErc721Contract*(self: Service, chainId: int, name: string): Erc721ContractDto =
let found = self.allErc721ContractsByChainId(chainId).filter(contract => contract.name.toLower == name.toLower)
result = if found.len > 0: found[0] else: nil
method buildTransaction*(self: Service, source: Address, value: Uint256, gas = "", gasPrice = "", isEIP1559Enabled = false, maxPriorityFeePerGas = "", maxFeePerGas = "", data = ""): TransactionDataDto =
result = TransactionDataDto(
source: source,
value: value.some,
gas: (if gas.isEmptyOrWhitespace: Quantity.none else: Quantity(cast[uint64](parseFloat(gas).toUInt64)).some),
gasPrice: (if gasPrice.isEmptyOrWhitespace: int.none else: gwei2Wei(parseFloat(gasPrice)).truncate(int).some),
data: data
)
if isEIP1559Enabled:
result.txType = "0x02"
result.maxPriorityFeePerGas = if maxFeePerGas.isEmptyOrWhitespace: Uint256.none else: gwei2Wei(parseFloat(maxPriorityFeePerGas)).some
result.maxFeePerGas = (if maxFeePerGas.isEmptyOrWhitespace: Uint256.none else: gwei2Wei(parseFloat(maxFeePerGas)).some)
else:
result.txType = "0x00"
method buildTokenTransaction*(self: Service, source, contractAddress: Address, gas = "", gasPrice = "", isEIP1559Enabled = false, maxPriorityFeePerGas = "", maxFeePerGas = ""): TransactionDataDto =
result = self.buildTransaction(source, 0.u256, gas, gasPrice, isEIP1559Enabled, maxPriorityFeePerGas, maxFeePerGas)
result.to = contractAddress.some

View File

@ -1,3 +1,17 @@
import web3/ethtypes
import ./dto/contract
import ./dto/method_dto
import ./dto/network
import ./dto/coder
import ./dto/edn_dto
import ./dto/transaction
export contract
export method_dto
export network
export coder
export edn_dto
export transaction
type
ServiceInterface* {.pure inheritable.} = ref object of RootObj
@ -8,3 +22,32 @@ method delete*(self: ServiceInterface) {.base.} =
method init*(self: ServiceInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method findByAddress*(self: ServiceInterface, contracts: seq[Erc20ContractDto], address: Address): Erc20ContractDto
{.base.} =
raise newException(ValueError, "No implementation available")
method findBySymbol*(self: ServiceInterface, contracts: seq[Erc20ContractDto], symbol: string): Erc20ContractDto
{.base.} =
raise newException(ValueError, "No implementation available")
method findContract*(self: ServiceInterface, chainId: int, name: string): ContractDto {.base.} =
raise newException(ValueError, "No implementation available")
method allErc20Contracts*(self: ServiceInterface): seq[Erc20ContractDto] {.base.} =
raise newException(ValueError, "No implementation available")
method allErc20ContractsByChainId*(self: ServiceInterface, chainId: int): seq[Erc20ContractDto] {.base.} =
raise newException(ValueError, "No implementation available")
method findErc20Contract*(self: ServiceInterface, chainId: int, symbol: string): Erc20ContractDto {.base.} =
raise newException(ValueError, "No implementation available")
method findErc20Contract*(self: ServiceInterface, chainId: int, address: Address): Erc20ContractDto {.base.} =
raise newException(ValueError, "No implementation available")
method allErc721ContractsByChainId*(self: ServiceInterface, chainId: int): seq[Erc721ContractDto] {.base.} =
raise newException(ValueError, "No implementation available")
method findErc721Contract*(self: ServiceInterface, chainId: int, name: string): Erc721ContractDto {.base.} =
raise newException(ValueError, "No implementation available")

View File

@ -7,9 +7,12 @@ from sugar import `=>`, `->`
import stint
from times import getTime, toUnix, nanosecond
import signing_phrases
from web3 import Address, fromHex
import web3/ethhexstrings
import ../../common/conversion as common_conversion
export common_conversion
proc decodeContentHash*(value: string): string =
if value == "":
return ""
@ -104,46 +107,6 @@ proc toStUInt*[bits: static[int]](flt: float, T: typedesc[StUint[bits]]): T =
else:
result = parse("0", StUint[bits])
proc toUInt256*(flt: float): UInt256 =
toStUInt(flt, StUInt[256])
proc toUInt64*(flt: float): StUInt[64] =
toStUInt(flt, StUInt[64])
proc eth2Wei*(eth: float, decimals: int = 18): UInt256 =
let weiValue = eth * parseFloat(alignLeft("1", decimals + 1, '0'))
weiValue.toUInt256
proc gwei2Wei*(gwei: float): UInt256 =
eth2Wei(gwei, 9)
proc wei2Eth*(input: Stuint[256], decimals: int = 18): string =
var one_eth = u256(10).pow(decimals) # fromHex(Stuint[256], "DE0B6B3A7640000")
var (eth, remainder) = divmod(input, one_eth)
let leading_zeros = "0".repeat(($one_eth).len - ($remainder).len - 1)
fmt"{eth}.{leading_zeros}{remainder}"
proc wei2Eth*(input: string, decimals: int): string =
try:
var input256: Stuint[256]
if input.contains("e+"): # we have a js string BN, ie 1e+21
let
inputSplit = input.split("e+")
whole = inputSplit[0].u256
remainder = u256(10).pow(inputSplit[1].parseInt)
input256 = whole * remainder
else:
input256 = input.u256
result = wei2Eth(input256, decimals)
except Exception as e:
error "Error parsing this wei value", input, msg=e.msg
result = "0"
proc wei2Gwei*(input: string): string =
result = wei2Eth(input, 9)
proc first*(jArray: JsonNode, fieldName, id: string): JsonNode =
if jArray == nil:
return nil
@ -184,16 +147,6 @@ proc find*[T](s: seq[T], pred: proc(x: T): bool {.closure.}, found: var bool): T
result = results[0]
found = true
proc parseAddress*(strAddress: string): Address =
fromHex(Address, strAddress)
proc isAddress*(strAddress: string): bool =
try:
discard parseAddress(strAddress)
except:
return false
return true
proc validateTransactionInput*(from_addr, to_addr, assetAddress, value, gas, gasPrice, data: string, isEIP1599Enabled: bool, maxPriorityFeePerGas, maxFeePerGas, uuid: string) =
if not isAddress(from_addr): raise newException(ValueError, "from_addr is not a valid ETH address")
if not isAddress(to_addr): raise newException(ValueError, "to_addr is not a valid ETH address")

View File

@ -3,11 +3,9 @@ import options
import status/statusgo_backend_new/network as status_network
import ./service_interface
import ./dto
import ./types
export service_interface
export dto, types
logScope:
topics = "network-service"

View File

@ -1,6 +1,6 @@
import dto, types
export dto
export dto, types
type
ServiceInterface* {.pure inheritable.} = ref object of RootObj

View File

@ -10,10 +10,10 @@ import json, tables, json_serialization
import status/statusgo_backend_new/stickers as status_stickers
import status/statusgo_backend_new/chat as status_chat
import status/statusgo_backend_new/transactions as transactions
import status/statusgo_backend_new/response_type
import status/statusgo_backend_new/eth
import ./dto/stickers
import ../ens/utils as ens_utils
import ../eth/service as eth_service
import ../settings/service as settings_service
import ../wallet_account/service as wallet_account_service
@ -126,7 +126,7 @@ QtObject:
buyToken = BuyToken(packId: packId, address: address, price: price)
buyTxAbiEncoded = stickerMktContract.methods["buyToken"].encodeAbi(buyToken)
approveAndCall = ApproveAndCall[100](to: stickerMktContract.address, value: price, data: DynamicBytes[100].fromHex(buyTxAbiEncoded))
self.eth_service.buildTokenTransaction(address, sntContract.address, gas, gasPrice, isEIP1559Enabled, maxPriorityFeePerGas, maxFeePerGas)
ens_utils.buildTokenTransaction(address, sntContract.address, gas, gasPrice, isEIP1559Enabled, maxPriorityFeePerGas, maxFeePerGas)
proc buyPack*(self: Service, packId: int, address, price, gas, gasPrice: string, isEIP1559Enabled: bool, maxPriorityFeePerGas: string, maxFeePerGas: string, password: string, success: var bool): string =
var
@ -147,11 +147,11 @@ QtObject:
result = sntContract.methods["approveAndCall"].send(tx, approveAndCall, password, success)
if success:
discard transactions.trackPendingTransaction(
self.transactionService.trackPendingTransaction(
result,
address,
$sntContract.address,
transactions.PendingTransactionType.BuyStickerPack,
$PendingTransactionTypeDto.BuyStickerPack,
$packId
)
@ -254,7 +254,7 @@ QtObject:
var pendingStickerPacks = initHashSet[int]()
if (pendingTransactions != ""):
for trx in pendingTransactions.parseJson{"result"}.getElems():
if trx["type"].getStr == $PendingTransactionType.BuyStickerPack:
if trx["type"].getStr == $PendingTransactionTypeDto.BuyStickerPack:
pendingStickerPacks.incl(trx["additionalData"].getStr.parseInt)
for stickerPack in availableStickers:

View File

@ -1,6 +1,5 @@
# include strformat, json
include ../../common/json_utils
include ../../../app/core/tasks/common
import ../eth/dto/contract
import ../eth/utils

View File

@ -1,7 +1,3 @@
include ../../common/json_utils
include ../../../app/core/tasks/common
import strutils
#################################################
# Async load transactions
#################################################

View File

@ -1,6 +1,17 @@
import json, strutils, stint
include ../../common/json_utils
type
PendingTransactionTypeDto* {.pure.} = enum
RegisterENS = "RegisterENS",
SetPubKey = "SetPubKey",
ReleaseENS = "ReleaseENS",
BuyStickerPack = "BuyStickerPack"
WalletTransfer = "WalletTransfer"
proc event*(self:PendingTransactionTypeDto):string =
result = "transaction:" & $self
type
TransactionDto* = ref object of RootObj
id*: string

View File

@ -1,23 +1,31 @@
import NimQml, chronicles, sequtils, sugar, stint, json
import NimQml, chronicles, sequtils, sugar, stint, strutils, json
import status/statusgo_backend_new/transactions as transactions
import status/statusgo_backend_new/wallet as status_wallet
import ../../../app/core/[main]
import ../../../app/core/tasks/[qt, threadpool]
import ../wallet_account/service as wallet_account_service
import ./service_interface, ./dto
import ./dto as transaction_dto
import ../eth/utils as eth_utils
export service_interface
export transaction_dto
logScope:
topics = "transaction-service"
include async_tasks
include ../../common/json_utils
# Signals which may be emitted by this service:
const SIGNAL_TRANSACTIONS_LOADED* = "transactionsLoaded"
type
TransactionMinedArgs* = ref object of Args
data*: string
transactionHash*: string
success*: bool
revertReason*: string
type
TransactionsLoadedArgs* = ref object of Args
transactions*: seq[TransactionDto]
@ -63,6 +71,14 @@ QtObject:
error "error: ", errDesription
return
proc trackPendingTransaction*(self: Service, hash: string, fromAddress: string, toAddress: string, trxType: string,
data: string) =
try:
discard transactions.trackPendingTransaction(hash, fromAddress, toAddress, trxType, data)
except Exception as e:
let errDesription = e.msg
error "error: ", errDesription
proc getTransfersByAddress*(self: Service, address: string, toBlock: Uint256, limit: int, loadMore: bool = false): seq[TransactionDto] =
try:
let limitAsHex = "0x" & eth_utils.stripLeadingZeros(limit.toHex)

View File

@ -1,22 +0,0 @@
import dto, stint
export dto
type
ServiceInterface* {.pure inheritable.} = ref object of RootObj
## Abstract class for this service access.
method delete*(self: ServiceInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method init*(self: ServiceInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method checkRecentHistory*(self: ServiceInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method getTransfersByAddress*(self: ServiceInterface, address: string, toBlock: Uint256, limit: int, loadMore: bool = false): seq[TransactionDto] {.base.} =
raise newException(ValueError, "No implementation available")
method getTransfersByAddressTemp*(self: ServiceInterface, address: string, toBlock: Uint256, limit: int, loadMore: bool = false): string {.base.} =
raise newException(ValueError, "No implementation available")

View File

@ -159,6 +159,9 @@ method buildTokens(
)
)
method getPrice*(self: Service, crypto: string, fiat: string): float64 =
return fetchPrice(crypto, fiat)
method fetchPrices(self: Service): Table[string, float64] =
let currency = self.settingsService.getCurrency()
var prices = {"ETH": fetchPrice("ETH", currency)}.toTable
@ -193,6 +196,8 @@ method getWalletAccounts*(self: Service): seq[WalletAccountDto] =
return toSeq(self.accounts.values)
method getWalletAccount*(self: Service, accountIndex: int): WalletAccountDto =
if(accountIndex < 0 or accountIndex >= self.getWalletAccounts().len):
return
return self.getWalletAccounts()[accountIndex]
method getCurrencyBalance*(self: Service): float64 =

View File

@ -47,3 +47,6 @@ method updateWalletAccount*(self: ServiceInterface, address: string, accountName
method toggleTokenVisible*(self: ServiceInterface, symbol: string) {.base.} =
raise newException(ValueError, "No implementation available")
method getPrice*(self: ServiceInterface, crypto: string, fiat: string): float64 {.base.} =
raise newException(ValueError, "No implementation available")

View File

@ -101,7 +101,6 @@ StatusModal {
anchors.top: separator.bottom
anchors.topMargin: 10
width: stack.width
isContact: root.isContact
onSelectedRecipientChanged: {
addressRequiredValidator.address = root.isRequested ? selectFromAccount.selectedAccount.address : selectRecipient.selectedRecipient.address
}

View File

@ -56,8 +56,7 @@ StatusAppTwoPanelLayout {
EnsView {
id: ensContainer
store: profileView.store
messageStore: profileView.globalStore.messageStore
ensUsernamesStore: profileView.store.ensUsernamesStore
contactsStore: profileView.store.contactsStore
profileContentWidth: _internal.profileContentWidth
}

View File

@ -15,24 +15,23 @@ ModalPopup {
//% "Primary username"
title: qsTrId("ens-primary-username")
property var ensUsernamesStore
property string newUsername: ""
onOpened: {
for(var i in ensNames.contentItem.children){
// Not Refactored Yet
// ensNames.contentItem.children[i].checked = ensNames.contentItem.children[i].text === profileModel.ens.preferredUsername
ensNames.contentItem.children[i].checked = ensNames.contentItem.children[i].text === popup.ensUsernamesStore.preferredUsername
}
}
StyledText {
id: lbl1
// Not Refactored Yet
// text: profileModel.ens.preferredUsername ?
// //% "Your messages are displayed to others with this username:"
// qsTrId("your-messages-are-displayed-to-others-with-this-username-")
// :
// //% "Once you select a username, you wont be able to disable it afterwards. You will only be able choose a different username to display."
// qsTrId("once-you-select-a-username--you-won-t-be-able-to-disable-it-afterwards--you-will-only-be-able-choose-a-different-username-to-display-")
text: popup.ensUsernamesStore.preferredUsername ?
//% "Your messages are displayed to others with this username:"
qsTrId("your-messages-are-displayed-to-others-with-this-username-")
:
//% "Once you select a username, you wont be able to disable it afterwards. You will only be able choose a different username to display."
qsTrId("once-you-select-a-username--you-won-t-be-able-to-disable-it-afterwards--you-will-only-be-able-choose-a-different-username-to-display-")
font.pixelSize: 15
wrapMode: Text.WordWrap
width: parent.width
@ -42,8 +41,7 @@ ModalPopup {
id: lbl2
anchors.top: lbl1.bottom
anchors.topMargin: Style.current.padding
// Not Refactored Yet
// text: profileModel.ens.preferredUsername
text: popup.ensUsernamesStore.preferredUsername
font.pixelSize: 17
font.weight: Font.Bold
}
@ -60,16 +58,14 @@ ModalPopup {
ListView {
anchors.fill: parent
// Not Refactored Yet
// model: profileModel.ens
model: root.ensUsernamesStore.ensUsernamesModel
spacing: 0
clip: true
id: ensNames
delegate: RadioDelegate {
id: radioDelegate
text: username
// Not Refactored Yet
// checked: profileModel.ens.preferredUsername === username
text: ensUsername
checked: popup.ensUsernamesStore.preferredUsername === ensUsername
contentItem: StyledText {
color: Style.current.textColor
@ -82,7 +78,7 @@ ModalPopup {
anchors.fill: parent
onClicked: {
parent.checked = true
newUsername = username;
newUsername = ensUsername;
}
}
}
@ -90,8 +86,7 @@ ModalPopup {
}
onNewUsernameChanged: {
// Not Refactored Yet
// btnSelectPreferred.state = newUsername === profileModel.ens.preferredUsername ? "inactive" : "active"
btnSelectPreferred.state = newUsername === popup.ensUsernamesStore.preferredUsername ? "inactive" : "active"
}
footer: Item {
@ -135,10 +130,9 @@ ModalPopup {
anchors.fill: parent
onClicked : {
if(btnSelectPreferred.state === "active"){
// Not Refactored Yet
// profileModel.ens.preferredUsername = newUsername;
// newUsername = "";
// popup.close();
popup.ensUsernamesStore.setPrefferedEnsUsername(newUsername);
newUsername = "";
popup.close();
}
}
}

View File

@ -0,0 +1,157 @@
import QtQuick 2.13
import utils 1.0
QtObject {
id: root
property var ensUsernamesModule
property var ensUsernamesModel: root.ensUsernamesModule ? ensUsernamesModule.model : []
property string pubkey: userProfile.pubKey
property string icon: userProfile.icon
property bool isIdenticon: userProfile.isIdenticon
property string preferredUsername: userProfile.preferredName
property string username: userProfile.username
property string gasPrice: root.ensUsernamesModule ? ensUsernamesModule.gasPrice : "0"
property var walletAccounts: walletSectionAccounts.model
function setPrefferedEnsUsername(ensName) {
if(!root.ensUsernamesModule)
return
ensUsernamesModule.setPrefferedEnsUsername(ensName)
}
function checkEnsUsernameAvailability(ensName, isStatus) {
if(!root.ensUsernamesModule)
return
ensUsernamesModule.checkEnsUsernameAvailability(ensName, isStatus)
}
function numOfPendingEnsUsernames() {
if(!root.ensUsernamesModule)
return 0
ensUsernamesModule.numOfPendingEnsUsernames()
}
function ensDetails(ensUsername) {
if(!root.ensUsernamesModule)
return ""
ensUsernamesModule.fetchDetailsForEnsUsername(ensUsername)
}
function fetchGasPrice() {
if(!root.ensUsernamesModule)
return "0"
ensUsernamesModule.fetchGasPrice()
}
function setPubKeyGasEstimate(ensUsername, address) {
if(!root.ensUsernamesModule)
return 0
return ensUsernamesModule.setPubKeyGasEstimate(ensUsername, address)
}
function setPubKey(ensUsername, address, gas, gasPrice, maxPriorityFeePerGas, maxFeePerGas, password) {
if(!root.ensUsernamesModule)
return ""
return ensUsernamesModule.setPubKey(ensUsername, address, gas, gasPrice, maxPriorityFeePerGas, maxFeePerGas, password)
}
function getEtherscanLink() {
if(!root.ensUsernamesModule)
return ""
return ensUsernamesModule.getEtherscanLink()
}
function getSigningPhrase() {
if(!root.ensUsernamesModule)
return ""
return ensUsernamesModule.getSigningPhrase()
}
function copyToClipboard(value) {
globalUtils.copyToClipboard(value)
}
function releaseEnsEstimate(ensUsername, address) {
if(!root.ensUsernamesModule)
return ""
return ensUsernamesModule.releaseEnsEstimate(ensUsername, address)
}
function releaseEns(ensUsername, address, gasLimit, gasPrice, password) {
if(!root.ensUsernamesModule)
return ""
return ensUsernamesModule.releaseEns(ensUsername, address, gasLimit, gasPrice, password)
}
function ensConnectOwnedUsername(name, isStatus) {
if(!root.ensUsernamesModule)
return
ensUsernamesModule.connectOwnedUsername(name, isStatus)
}
function getEnsRegisteredAddress() {
if(!root.ensUsernamesModule)
return ""
return ensUsernamesModule.getEnsRegisteredAddress()
}
function registerEnsGasEstimate(ensUsername, address) {
if(!root.ensUsernamesModule)
return 0
return ensUsernamesModule.registerEnsGasEstimate(ensUsername, address)
}
function registerEns(ensUsername, address, gasLimit, tipLimit, overallLimit, gasPrice, password) {
if(!root.ensUsernamesModule)
return ""
return ensUsernamesModule.registerEns(ensUsername, address, gasLimit, tipLimit, overallLimit, gasPrice, password)
}
function getEnsRegistry() {
if(!root.ensUsernamesModule)
return ""
return ensUsernamesModule.getEnsRegistry()
}
function getSntBalance() {
if(!root.ensUsernamesModule)
return ""
return ensUsernamesModule.getSNTBalance()
}
function getWalletDefaultAddress() {
if(!root.ensUsernamesModule)
return ""
return ensUsernamesModule.getWalletDefaultAddress()
}
function getCurrentCurrency() {
if(!root.ensUsernamesModule)
return ""
return ensUsernamesModule.getCurrentCurrency()
}
function getFiatValue(balance, cryptoSymbo, fiatSymbol) {
if(!root.ensUsernamesModule)
return ""
return ensUsernamesModule.getFiatValue(balance, cryptoSymbo, fiatSymbol)
}
function getGasEthValue(gweiValue, gasLimit) {
if(!root.ensUsernamesModule)
return ""
return ensUsernamesModule.getGasEthValue(gweiValue, gasLimit)
}
function getStatusToken() {
if(!root.ensUsernamesModule)
return ""
return ensUsernamesModule.getStatusToken()
}
}

View File

@ -43,30 +43,12 @@ QtObject {
privacyModule: profileSectionModuleInst.privacyModule
}
// Not Refactored Yet
// property var chatsModelInst: chatsModel
// Not Refactored Yet
// property var utilsModelInst: utilsModel
// Not Refactored Yet
// property var walletModelInst: walletModel
// Not Refactored Yet
// property var nodeModelInst: nodeModel
property EnsUsernamesStore ensUsernamesStore: EnsUsernamesStore {
ensUsernamesModule: profileSectionModuleInst.ensUsernamesModule
}
// Not Refactored Yet
// property var ens: profileModelInst.ens
property var dappList: dappPermissionsModule.dapps
property var permissionList: dappPermissionsModule.permissions
// Not Refactored Yet
// property var mutedChatsContacts: profileModelInst.mutedChats.contacts
// property var mutedChats: profileModelInst.mutedChats.chats
// Not Refactored Yet
property string ensRegisterAddress: "" //utilsModelInst.ensRegisterAddress
// Not Refactored Yet
property string etherscanLink: "" //walletModelInst.utilsView.etherscanLink
// Not Refactored Yet
// property string preferredUsername: userProfile.preferredName // was: profileModelInst.ens.preferredUsername
// property string firstEnsUsername: userProfile.firstEnsName // was: profileModelInst.ens.firstEnsUsername
property int profile_id: 0
property int contacts_id: 1
@ -188,90 +170,6 @@ QtObject {
dappPermissionsModule.fetchDapps()
}
function ensDetails(username) {
// Not Refactored Yet
// profileModelInst.ens.details(username)
}
function ensPendingLen() {
// Not Refactored Yet
// return profileModelInst.ens.pendingLen()
}
function validateEns(ensName, isStatus) {
// Not Refactored Yet
// profileModelInst.ens.validate(ensName, isStatus)
}
function registerEnsGasEstimate(username, address) {
// Not Refactored Yet
// return profileModelInst.ens.registerENSGasEstimate(username, address)
}
function registerEns(username, address, gasLimit, tipLimit, overallLimit, gasPrice, password) {
// Not Refactored Yet
// return profileModelInst.ens.registerENS(username,
// address, gasLimit, tipLimit, overallLimit, gasPrice, password)
}
function getEnsUsernameRegistrar() {
// Not Refactored Yet
// return profileModelInst.ens.getUsernameRegistrar()
}
function getEnsRegistry() {
// Not Refactored Yet
// return profileModelInst.ens.getENSRegistry()
}
function releaseEnsEstimate(username, address) {
// Not Refactored Yet
// return profileModelInst.ens.releaseEstimate(username, address)
}
function releaseEns(username, address, gasLimit, gasPrice, password) {
// Not Refactored Yet
// return profileModelInst.ens.release(username, address, gasLimit, gasPrice, password)
}
function getGasPrice() {
// Not Refactored Yet
// walletModelInst.gasView.getGasPrice()
}
function getGasPricePredictions() {
// Not Refactored Yet
// walletModelInst.gasView.getGasPricePredictions()
}
function ensConnectOwnedUsername(name, isStatus) {
// Not Refactored Yet
// profileModelInst.ens.connectOwnedUsername(name, isStatus)
}
function getWalletDefaultAddress() {
// Not Refactored Yet
// return walletModelInst.getDefaultAddress()
}
function getSntBalance() {
// Not Refactored Yet - This should be fetched from corresponding module, not from the global Utils
// return utilsModelInst.getSNTBalance()
}
function getNetworkName() {
// Not Refactored Yet
// return utilsModelInst.getNetworkName()
}
function setBloomLevel(mode) {
nodeModelInst.setBloomLevel(mode)
}
function setWakuV2LightClient(mode) {
nodeModelInst.setWakuV2LightClient(mode)
}
function getCurrentVersion() {
return aboutModuleInst.getCurrentVersion()
}
@ -283,24 +181,4 @@ QtObject {
function checkForUpdates() {
aboutModuleInst.checkForUpdates()
}
function setPubKeyGasEstimate(username, address) {
// Not Refactored Yet
// return profileModelInst.ens.setPubKeyGasEstimate(username, address)
}
function setPubKey(username, address, gasLimit, gasPrice, password) {
// Not Refactored Yet
// return profileModelInst.ens.setPubKey(username, address, gasLimit, gasPrice, password)
}
function userNameOrAlias(pk) {
// Not Refactored Yet
// return chatsModelInst.userNameOrAlias(pk);
}
function generateIdenticon(pk) {
// Not Refactored Yet
// return utilsModelInst.generateIdenticon(pk);
}
}

View File

@ -12,7 +12,7 @@ import shared.status 1.0
Item {
id: root
property var store
property var ensUsernamesStore
property var contactsStore
property string username: ""
property string walletAddress: "-"
@ -50,15 +50,17 @@ Item {
}
Connections {
target: root.store.ens
target: root.ensUsernamesStore.ensUsernamesModule
onDetailsObtained: {
if(username != ensName) return;
if(username != ensName)
return;
walletAddressLbl.subTitle = address;
keyLbl.subTitle = pubkey.substring(0, 20) + "..." + pubkey.substring(pubkey.length - 20);
walletAddressLbl.visible = true;
keyLbl.visible = true;
releaseBtn.visible = isStatus
releaseBtn.enabled = (Date.now() / 1000) > expirationTime && expirationTime > 0 && root.store.preferredUsername != username
releaseBtn.enabled = (Date.now() / 1000) > expirationTime && expirationTime > 0 &&
root.ensUsernamesStore.preferredUsername != username
expiration = new Date(expirationTime * 1000).getTime()
}
onLoading: {
@ -80,7 +82,7 @@ Item {
icon.name: "copy"
tooltip.text: qsTr("Copied to clipboard!")
iconButton.onClicked: {
root.store.copyToClipboard(subTitle)
root.ensUsernamesStore.copyToClipboard(subTitle)
tooltip.visible = !tooltip.visible
}
}
@ -93,7 +95,7 @@ Item {
icon.name: "copy"
tooltip.text: qsTr("Copied to clipboard!")
iconButton.onClicked: {
root.store.copyToClipboard(subTitle)
root.ensUsernamesStore.copyToClipboard(subTitle)
tooltip.visible = !tooltip.visible
}
}
@ -101,9 +103,10 @@ Item {
Component {
id: transactionDialogComponent
StatusETHTransactionModal {
ensUsernamesStore: root.ensUsernamesStore
contactsStore: root.contactsStore
onOpened: {
root.store.getGasPricePredictions()
root.ensUsernamesStore.fetchGasPrice()
}
title: qsTr("Connect username with your pubkey")
onClosed: {
@ -111,10 +114,10 @@ Item {
}
estimateGasFunction: function(selectedAccount) {
if (username === "" || !selectedAccount) return 100000;
return root.store.releaseEnsEstimate(Utils.removeStatusEns(username), selectedAccount.address)
return root.ensUsernamesStore.releaseEnsEstimate(Utils.removeStatusEns(username), selectedAccount.address)
}
onSendTransaction: function(selectedAddress, gasLimit, gasPrice, password) {
return root.store.releaseEns(username,
return root.ensUsernamesStore.releaseEns(username,
selectedAddress,
gasLimit,
gasPrice,

View File

@ -20,7 +20,8 @@ Item {
signal addBtnClicked()
signal selectEns(string username)
property var store
property var ensUsernamesStore
property int profileContentWidth
// Defaults to show message
@ -34,13 +35,11 @@ Item {
property string authorPrevMsg: "1"
property bool isText: true
property var clickMessage: function(){}
property string identicon: store.identicon
//property string identicon: userProfile.icon
property int timestamp: 1577872140
property var messageStore
function shouldDisplayExampleMessage(){
return store.ens.rowCount() > 0 && store.ensPendingLen() !== store.ens.rowCount() && store.preferredUsername !== ""
return root.ensUsernamesStore.ensUsernamesModel.count > 0 &&
root.ensUsernamesStore.numOfPendingEnsUsernames() !== root.ensUsernamesStore.ensUsernamesModel &&
store.ensUsernamesStore.preferredUsername !== ""
}
anchors.fill: parent
@ -94,7 +93,7 @@ Item {
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: selectEns(model.username)
onClicked: selectEns(model.ensUsername)
}
Rectangle {
@ -117,8 +116,8 @@ Item {
}
Loader {
sourceComponent: model.username.endsWith(".stateofus.eth") ? statusENS : normalENS
property string username: model.username
sourceComponent: model.ensUsername.endsWith(".stateofus.eth") ? statusENS : normalENS
property string username: model.ensUsername
property bool isPending: model.isPending
active: true
anchors.left: circle.right
@ -129,6 +128,7 @@ Item {
ENSPopup {
id: ensPopup
ensUsernamesStore: root.ensUsernamesStore
}
StatusBaseText {
@ -209,7 +209,7 @@ Item {
ListView {
id: lvEns
anchors.fill: parent
model: root.store.ens
model: root.ensUsernamesStore.ensUsernamesModel
spacing: 10
clip: true
delegate: ensDelegate
@ -225,7 +225,8 @@ Item {
StatusBaseText {
id: chatSettingsLabel
visible: root.store.ens.rowCount() > 0 && root.store.ensPendingLen() != root.store.ens.rowCount()
visible: root.ensUsernamesStore.ensUsernamesModel.count > 0 &&
root.ensUsernamesStore.numOfPendingEnsUsernames() != root.ensUsernamesStore.ensUsernamesModel.count
//% "Chat settings"
text: qsTrId("chat-settings")
anchors.left: parent.left
@ -258,7 +259,7 @@ Item {
id: usernameLabel2
visible: chatSettingsLabel.visible
//% "None selected"
text: root.store.preferredUsername || qsTrId("none-selected")
text: root.ensUsernamesStore.preferredUsername || qsTrId("none-selected")
anchors.left: usernameLabel.right
anchors.leftMargin: Style.current.padding
font.pixelSize: 14
@ -284,10 +285,10 @@ Item {
anchors.leftMargin: Style.current.padding
anchors.top: parent.top
anchors.topMargin: 20
// isCurrentUser: root.isCurrentUser
// profileImage: root.messageStore.profileImageSource
// isMessage: root.messageStore.isMessage
// identiconImageSource: root.messageStore.identicon
icon: root.ensUsernamesStore.icon
isIdenticon: root.ensUsernamesStore.isIdenticon
onClickMessage: {
root.parent.clickMessage(isProfileClick, isSticker, isImage, image, emojiOnly, hideEmojiPicker, isReply);
}
@ -295,19 +296,14 @@ Item {
UsernameLabel {
id: chatName
label.text: "@" + (root.store.preferredUsername.replace(".stateofus.eth", ""))
label.color: Style.current.blue
anchors.leftMargin: 20
anchors.top: parent.top
anchors.topMargin: 0
anchors.left: chatImage.right
// isCurrentUser: root.messageStore.isCurrentUser
// userName: root.messageStore.userName
// localName: root.messageStore.localName
// displayUserName: root.messageStore.displayUserName
onClickMessage: {
root.parent.clickMessage(true, false, false, null, false, false, false);
}
displayName: "@" + (root.ensUsernamesStore.preferredUsername.replace(".stateofus.eth", ""))
localName: ""
amISender: true
}
Rectangle {
@ -343,8 +339,8 @@ Item {
anchors.bottomMargin: Style.current.padding
anchors.right: chatBox.right
anchors.rightMargin: Style.current.padding
//timestamp: root.timestamp
visible: root.messageStore.isMessage
timestamp: new Date().getTime()
visible: true
}
StatusBaseText {
@ -360,10 +356,7 @@ Item {
Connections {
target: root.store.ens
onPreferredUsernameChanged: {
messagesShownAs.visible = shouldDisplayExampleMessage()
}
target: root.ensUsernamesStore.ensUsernamesModule
onUsernameConfirmed: {
messagesShownAs.visible = shouldDisplayExampleMessage()
chatSettingsLabel.visible = true

View File

@ -15,7 +15,7 @@ import shared.controls 1.0
Item {
id: root
property var store
property var ensUsernamesStore
property var contactsStore
property int profileContentWidth
@ -30,7 +30,7 @@ Item {
property string ensStatus: ""
property var validateENS: Backpressure.debounce(root, 500, function (ensName, isStatus){
store.validateEns(ensName, isStatus)
root.ensUsernamesStore.checkEnsUsernameAvailability(ensName, isStatus)
});
function validate(ensUsername) {
@ -61,9 +61,10 @@ Item {
Component {
id: transactionDialogComponent
StatusETHTransactionModal {
ensUsernamesStore: root.ensUsernamesStore
contactsStore: root.contactsStore
onOpened: {
root.store.getGasPrice()
root.ensUsernamesStore.fetchGasPrice()
}
title: qsTr("Connect username with your pubkey")
onClosed: {
@ -71,13 +72,15 @@ Item {
}
estimateGasFunction: function(selectedAccount) {
if (ensUsername.text === "" || !selectedAccount) return 80000;
return root.store.setPubKeyGasEstimate(ensUsername.text + (isStatus ? ".stateofus.eth" : "" ), selectedAccount.address)
return root.ensUsernamesStore.setPubKeyGasEstimate(ensUsername.text + (isStatus ? ".stateofus.eth" : "" ), selectedAccount.address)
}
onSendTransaction: function(selectedAddress, gasLimit, gasPrice, password) {
return root.store.setPubKey(ensUsername.text + (isStatus ? ".stateofus.eth" : "" ),
return root.ensUsernamesStore.setPubKey(ensUsername.text + (isStatus ? ".stateofus.eth" : "" ),
selectedAddress,
gasLimit,
gasPrice,
"",
"",
password)
}
onSuccess: function(){
@ -148,25 +151,25 @@ Item {
}
Connections {
target: mainModule
onResolvedENS: {
target: root.ensUsernamesStore.ensUsernamesModule
onUsernameAvailabilityChecked: {
if(!validate(ensUsername.text)) return;
valid = false;
loading = false;
ensStatus = ensResult;
switch(ensResult){
ensStatus = availabilityStatus;
switch(availabilityStatus){
case "available":
case "owned":
case "connected":
case "connected-different-key":
valid = true;
validationMessage = Constants.ensState[ensResult]
validationMessage = Constants.ensState[availabilityStatus]
break;
case "taken":
validationMessage = Constants.ensState[!isStatus ? 'taken-custom' : 'taken']
break;
case "already-connected":
validationMessage = Constants.ensState[ensResult]
validationMessage = Constants.ensState[availabilityStatus]
break;
}
}
@ -189,7 +192,7 @@ Item {
if(!valid) return;
if(ensStatus === Constants.ens_connected){
root.store.ensConnectOwnedUsername(ensUsername.text, isStatus);
root.ensUsernamesStore.ensConnectOwnedUsername(ensUsername.text, isStatus);
continueClicked(ensStatus, ensUsername.text)
return;
}

View File

@ -14,10 +14,10 @@ import StatusQ.Components 0.1
Item {
id: root
property var store
property string username: ""
readonly property string ensRegistry: "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e"
property var ensUsernamesStore
property var contactsStore
property string username: ""
signal backBtnClicked();
signal usernameRegistered(userName: string);
@ -45,14 +45,16 @@ Item {
this.active = false // kill an opened instance
}
sourceComponent: StatusSNTTransactionModal {
store: root.ensUsernamesStore
contactsStore: root.contactsStore
assetPrice: "10"
contractAddress: root.store.ensRegisterAddress
contractAddress: root.ensUsernamesStore.getEnsRegisteredAddress()
estimateGasFunction: function(selectedAccount, uuid) {
if (username === "" || !selectedAccount) return 380000;
return root.store.registerEnsGasEstimate(username, selectedAccount.address)
return root.ensUsernamesStore.registerEnsGasEstimate(username, selectedAccount.address)
}
onSendTransaction: function(selectedAddress, gasLimit, gasPrice, tipLimit, overallLimit, password) {
return root.store.registerEns(
return root.ensUsernamesStore.registerEns(
username,
selectedAddress,
gasLimit,
@ -165,7 +167,7 @@ Item {
StatusBaseText {
//% "%1 (Status UsernameRegistrar)."
text: qsTrId("-1--status-usernameregistrar--").arg(root.store.getEnsUsernameRegistrar())
text: qsTrId("-1--status-usernameregistrar--").arg(root.ensUsernamesStore.getEnsRegisteredAddress())
wrapMode: Text.WordWrap
anchors.left: parent.left
anchors.right: parent.right
@ -175,7 +177,9 @@ Item {
StatusBaseText {
//% "<a href='%1%2'>Look up on Etherscan</a>"
text: qsTrId("-a-href---1-2--look-up-on-etherscan--a-").arg(root.store.etherscanLink.replace("/tx", "/address")).arg(root.store.getEnsUsernameRegistrar())
text: qsTrId("-a-href---1-2--look-up-on-etherscan--a-")
.arg(root.ensUsernamesStore.getEtherscanLink())
.arg(root.ensUsernamesStore.getEnsRegisteredAddress())
anchors.left: parent.left
anchors.right: parent.right
onLinkActivated: Global.openLink(link)
@ -189,7 +193,7 @@ Item {
StatusBaseText {
//% "%1 (ENS Registry)."
text: qsTrId("-1--ens-registry--").arg(root.ensRegistry)
text: qsTrId("-1--ens-registry--").arg(root.ensUsernamesStore.getEnsRegistry())
wrapMode: Text.WordWrap
anchors.left: parent.left
anchors.right: parent.right
@ -199,7 +203,9 @@ Item {
StatusBaseText {
//% "<a href='%1%2'>Look up on Etherscan</a>"
text: qsTrId("-a-href---1-2--look-up-on-etherscan--a-").arg(root.store.etherscanLink.replace("/tx", "/address")).arg(root.ensRegistry)
text: qsTrId("-a-href---1-2--look-up-on-etherscan--a-")
.arg(root.ensUsernamesStore.getEtherscanLink())
.arg(root.ensUsernamesStore.getEnsRegistry())
anchors.left: parent.left
anchors.right: parent.right
onLinkActivated: Global.openLink(link)
@ -270,11 +276,11 @@ Item {
id: walletAddressLbl
//% "Wallet address"
title: qsTrId("wallet-address")
subTitle: root.store.getWalletDefaultAddress()
subTitle: root.ensUsernamesStore.getWalletDefaultAddress()
tooltip.text: qsTr("Copied to clipboard!")
icon.name: "copy"
iconButton.onClicked: {
root.store.copyToClipboard(subTitle)
root.ensUsernamesStore.copyToClipboard(subTitle)
tooltip.visible = !tooltip.visible
}
anchors.top: ensUsername.bottom
@ -286,13 +292,13 @@ Item {
//% "Key"
title: qsTrId("key")
subTitle: {
let pubKey = root.store.pubKey;
let pubKey = root.ensUsernamesStore.pubkey;
return pubKey.substring(0, 20) + "..." + pubKey.substring(pubKey.length - 20);
}
tooltip.text: qsTr("Copied to clipboard!")
icon.name: "copy"
iconButton.onClicked: {
root.store.copyToClipboard(root.store.pubKey)
root.ensUsernamesStore.copyToClipboard(root.ensUsernamesStore.pubkey)
tooltip.visible = !tooltip.visible
}
anchors.top: walletAddressLbl.bottom
@ -345,7 +351,7 @@ Item {
id: image1
height: 50
width: height
source: Style.svg("status-logo")
source: Style.png("tokens/SNT")
sourceSize: Qt.size(width, height)
}
@ -378,12 +384,12 @@ Item {
anchors.bottomMargin: Style.current.padding
anchors.right: parent.right
anchors.rightMargin: Style.current.padding
text: parseFloat(root.store.getSntBalance()) < 10 ?
text: parseFloat(root.ensUsernamesStore.getSntBalance()) < 10 ?
//% "Not enough SNT"
qsTrId("not-enough-snt") :
//% "Register"
qsTrId("ens-register")
enabled: parseFloat(root.store.getSntBalance()) >= 10 && termsAndConditionsCheckbox.checked
enabled: parseFloat(root.ensUsernamesStore.getSntBalance()) >= 10 && termsAndConditionsCheckbox.checked
onClicked: localAccountSensitiveSettings.isWalletEnabled ? transactionDialog.open() : confirmationPopup.open()
}

View File

@ -11,10 +11,10 @@ import "../stores"
Item {
id: ensView
property var store
property EnsUsernamesStore ensUsernamesStore
property var contactsStore
property var messageStore
property int profileContentWidth
property bool showSearchScreen: false
property string addedUsername: ""
@ -29,7 +29,7 @@ Item {
signal goToList();
function goToStart(){
if(ensView.store.ens.rowCount() > 0 && Global.networkGuarded){
if(ensView.ensUsernamesStore.ensUsernamesModel.count > 0 && Global.networkGuarded){
goToList();
} else {
goToWelcome();
@ -213,7 +213,7 @@ Item {
Component {
id: welcome
EnsWelcomeView {
username: ensView.store.username
username: ensView.ensUsernamesStore.username
onStartBtnClicked: next(null)
profileContentWidth: ensView.profileContentWidth
}
@ -222,7 +222,7 @@ Item {
Component {
id: search
EnsSearchView {
store: ensView.store
ensUsernamesStore: ensView.ensUsernamesStore
contactsStore: ensView.contactsStore
profileContentWidth: ensView.profileContentWidth
onContinueClicked: {
@ -243,7 +243,8 @@ Item {
Component {
id: termsAndConditions
EnsTermsAndConditionsView {
store: ensView.store
ensUsernamesStore: ensView.ensUsernamesStore
contactsStore: ensView.contactsStore
username: selectedUsername
onBackBtnClicked: back();
onUsernameRegistered: done(userName);
@ -284,12 +285,12 @@ Item {
Component {
id: list
EnsListView {
store: ensView.store
messageStore: ensView.messageStore
ensUsernamesStore: ensView.ensUsernamesStore
profileContentWidth: ensView.profileContentWidth
onAddBtnClicked: next("search")
onSelectEns: {
ensView.store.ensDetails(username)
ensView.ensUsernamesStore.ensDetails(username)
selectedUsername = username;
next("details")
}
@ -299,7 +300,7 @@ Item {
Component {
id: details
EnsDetailsView {
store: ensView.store
ensUsernamesStore: ensView.ensUsernamesStore
contactsStore: ensView.contactsStore
username: selectedUsername
onBackBtnClicked: back();
@ -318,14 +319,14 @@ Item {
}
Connections {
target: ensView.store.ens
target: ensView.ensUsernamesStore.ensUsernamesModule
onTransactionWasSent: {
//% "Transaction pending..."
Global.toastMessage.title = qsTrId("ens-transaction-pending")
Global.toastMessage.source = Style.svg("loading")
Global.toastMessage.iconColor = Style.current.primary
Global.toastMessage.iconRotates = true
Global.toastMessage.link = `${root.store.etherscanLink}/${txResult}`
Global.toastMessage.link = `${ensView.ensUsernamesStore.getEtherscanLink()}/${txResult}`
Global.toastMessage.open()
}
onTransactionCompleted: {
@ -355,7 +356,7 @@ Item {
Global.toastMessage.source = Style.svg("block-icon")
Global.toastMessage.iconColor = Style.current.danger
}
Global.toastMessage.link = `${root.store.etherscanLink}/${txHash}`
Global.toastMessage.link = `${ensView.ensUsernamesStore.getEtherscanLink()}/${txHash}`
Global.toastMessage.open()
}
}

View File

@ -37,17 +37,16 @@ Item {
function resolveEns() {
if (selectedContact.ensVerified) {
root.isResolvedAddress = false
ensResolver.resolveEns(selectedContact.name)
ensResolver.resolveEns(selectedContact.alias)
}
}
// This should be removed most likely.
// onContactsChanged: {
// if (root.readOnly) {
// return
// }
// root.selectedContact = { name: selectAContact }
// }
Component.onCompleted: {
if (root.readOnly) {
return
}
root.selectedContact = { alias: selectAContact }
}
onSelectedContactChanged: validate()
@ -55,8 +54,8 @@ Item {
if (!selectedContact) {
return root.isValid
}
let isValidAddress = Utils.isValidAddress(selectedContact.address)
let isDefaultValue = selectedContact.name === selectAContact
let isValidAddress = Utils.isValidAddress(selectedContact.publicKey)
let isDefaultValue = selectedContact.alias === selectAContact
let isValid = (selectedContact.ensVerified && isValidAddress) || isPending || isValidAddress
select.validationError = ""
if (!isValid && !isDefaultValue &&
@ -76,7 +75,7 @@ Item {
visible: root.readOnly
width: parent.width
//% "No contact selected"
text: (root.selectedContact && root.selectedContact.name) ? root.selectedContact.name : qsTrId("no-contact-selected")
text: (root.selectedContact && root.selectedContact.alias) ? root.selectedContact.alias : qsTrId("no-contact-selected")
textField.leftPadding: 14
textField.topPadding: 18
textField.bottomPadding: 18
@ -103,15 +102,15 @@ Item {
anchors.left: parent.left
anchors.leftMargin: 14
anchors.verticalCenter: parent.verticalCenter
image.width: (!!selectedContact && !!selectedContact.identicon) ? 32 : 0
image.width: (!!selectedContact && !!selectedContact.displayIcon) ? 32 : 0
image.height: 32
image.source: (!!selectedContact && !!selectedContact.identicon) ? selectedContact.identicon : ""
image.isIdenticon: true
active: !!selectedContact && !!selectedContact.identicon
image.source: (!!selectedContact && !!selectedContact.displayIcon) ? selectedContact.displayIcon : ""
image.isIdenticon: (!!selectedContact && !!selectedContact.isDisplayIconIdenticon) ? selectedContact.isDisplayIconIdenticon : true
active: !!selectedContact && !!selectedContact.displayIcon
}
StatusBaseText {
id: selectedTextField
text: !!selectedContact ? selectedContact.name : ""
text: !!selectedContact ? selectedContact.alias : ""
anchors.left: iconImg.right
anchors.leftMargin: 4
anchors.verticalCenter: parent.verticalCenter
@ -148,12 +147,12 @@ Item {
onResolved: {
root.isResolvedAddress = true
var selectedContact = root.selectedContact
selectedContact.address = resolvedAddress
selectedContact.publicKey = resolvedAddress
root.selectedContact = selectedContact
}
onIsPendingChanged: {
if (isPending) {
root.selectedContact.address = ""
root.selectedContact.publicKey = ""
}
}
}
@ -165,6 +164,8 @@ Item {
property bool isFirstItem: index === 0
property bool isLastItem: index === root.contactsStore.myContactsModel.count - 1
property var currentContact: Utils.getContactDetailsAsJson(pubKey)
width: parent.width
height: visible ? 72 : 0
StatusSmartIdenticon {
@ -172,8 +173,8 @@ Item {
anchors.left: parent.left
anchors.leftMargin: Style.current.padding
anchors.verticalCenter: parent.verticalCenter
image.source: identicon
image.isIdenticon: true
image.source: currentContact.displayIcon
image.isIdenticon: currentContact.isDisplayIconIdenticon
}
Column {
anchors.left: iconImg.right
@ -181,7 +182,7 @@ Item {
anchors.verticalCenter: parent.verticalCenter
StatusBaseText {
text: name
text: currentContact.alias
font.pixelSize: 15
color: Theme.palette.directColor1
height: 22
@ -189,14 +190,14 @@ Item {
Row {
StatusBaseText {
text: alias + " • "
visible: ensVerified
text: currentContact.name + " • "
visible: currentContact.ensVerified
color: Theme.palette.baseColor1
font.pixelSize: 12
height: 16
}
StatusBaseText {
text: address
text: currentContact.publicKey
width: 85
elide: Text.ElideMiddle
color: Theme.palette.baseColor1
@ -212,7 +213,7 @@ Item {
cursorShape: Qt.PointingHandCursor
anchors.fill: itemContainer
onClicked: {
root.selectedContact = { address, name, alias, isContact, identicon, ensVerified }
root.selectedContact = itemContainer.currentContact
resolveEns()
select.selectMenu.close()
}
@ -220,10 +221,3 @@ Item {
}
}
}
/*##^##
Designer {
D{i:0;autoSize:true;height:480;width:640}
}
##^##*/

View File

@ -267,10 +267,9 @@ Item {
text: qsTrId("optimal")
price: {
if (!eip1599Enabled) {
const price = gasPrice
// Setting the gas price field here because the binding didn't work
inputGasPrice.text = price
return price
inputGasPrice.text = root.gasPrice
return root.gasPrice
}
return formatDec(suggestedFees.maxFeePerGasM, 6)
@ -284,7 +283,7 @@ Item {
inputPerGasTipLimit.text = formatDec(suggestedFees.maxPriorityFeePerGas, 2);
inputGasPrice.text = formatDec(suggestedFees.maxFeePerGasM, 2);
} else {
inputGasPrice.text = price
inputGasPrice.text = root.gasPrice
}
root.updateGasEthValue()
root.checkLimits()

View File

@ -16,7 +16,6 @@ Item {
property var contactsStore
property var accounts
property int currentIndex
property int inputWidth: 272
property int sourceSelectWidth: 136
property alias label: txtLabel.text
@ -24,7 +23,6 @@ Item {
property alias additionalInfo: txtAddlInfo.text
property var selectedRecipient
property bool readOnly: false
property bool isContact: false
height: inpAddress.height + txtLabel.height
//% "Invalid ethereum address"
readonly property string addressValidationError: qsTrId("invalid-ethereum-address")
@ -149,6 +147,9 @@ Item {
AddressInput {
id: inpAddress
contactsStore: root.contactsStore
width: root.inputWidth
input.label: ""
input.readOnly: root.readOnly
@ -175,7 +176,6 @@ Item {
width: root.inputWidth
dropdownWidth: parent.width
readOnly: root.readOnly
isContact: root.isContact
Layout.preferredWidth: selAddressSource.visible ? root.inputWidth : parent.width
Layout.alignment: Qt.AlignTop
Layout.fillWidth: true
@ -258,10 +258,3 @@ Item {
}
}
/*##^##
Designer {
D{i:0;autoSize:true;height:480;width:640}
}
##^##*/

View File

@ -29,7 +29,7 @@ Rectangle {
if (!gasLimit) {
return 0
}
return formatDec(parseFloat(getGasEthValue(price, gasLimit)), 6)
return formatDec(parseFloat(getGasEthValue(gasRectangle.price, gasLimit)), 6)
}
property double fiatValue: getFiatValue(ethValue, "ETH", defaultCurrency)
signal checked()

View File

@ -12,6 +12,7 @@ Item {
width: chatName.width + (ensOrAlias.visible ? ensOrAlias.width + ensOrAlias.anchors.leftMargin : 0)
property alias label: chatName
property var messageContextMenu
property string displayName
property string localName
property bool amISender
@ -40,11 +41,11 @@ Item {
root.isHovered = false
}
onClicked: {
if (!!messageContextMenu) {
if (!!root.messageContextMenu) {
// Set parent, X & Y positions for the messageContextMenu
messageContextMenu.parent = root
messageContextMenu.setXPosition = function() { return 0}
messageContextMenu.setYPosition = function() { return root.height + 4}
root.messageContextMenu.parent = root
root.messageContextMenu.setXPosition = function() { return 0}
root.messageContextMenu.setYPosition = function() { return root.height + 4}
}
root.clickMessage(true);
}

View File

@ -114,13 +114,11 @@ ModalPopup {
id: selectRecipient
accounts: root.store.accounts
contactsStore: root.contactsStore
currentIndex: index
//% "Recipient"
label: qsTrId("recipient")
anchors.top: separator.bottom
anchors.topMargin: 10
width: stack.width
isContact: root.isContact
onSelectedRecipientChanged: if (isValid) { gasSelector.estimateGas() }
}
}

View File

@ -135,7 +135,6 @@ StatusModal {
// accounts: root.store.walletModelInst.accountsView.accounts
contactsStore: root.contactsStore
selectedRecipient: root.selectedRecipient
currentIndex: index
readOnly: true
}
}

View File

@ -16,6 +16,7 @@ import shared.controls 1.0
ModalPopup {
id: root
property var ensUsernamesStore
property var contactsStore
readonly property var asset: {"name": "Ethereum", "symbol": "ETH"}
@ -26,42 +27,36 @@ ModalPopup {
property var onSendTransaction: (function(userAddress, gasLimit, gasPrice, password){ return ""; })
property var onSuccess: (function(){})
Component.onCompleted: {
// Not Refactored Yet
// RootStore.walletModelInst.gasView.getGasPricePredictions()
}
height: 540
function sendTransaction() {
// Not Refactored Yet
// try {
// let responseStr = RootStore.profileModelInst.ens.setPubKey(root.ensUsername,
// selectFromAccount.selectedAccount.address,
// gasSelector.selectedGasLimit,
// gasSelector.eip1599Enabled ? "" : gasSelector.selectedGasPrice,
// gasSelector.selectedTipLimit,
// gasSelector.selectedOverallLimit,
// transactionSigner.enteredPassword)
// let response = JSON.parse(responseStr)
try {
let responseStr = root.ensUsernamesStore.setPubKey(root.ensUsername,
selectFromAccount.selectedAccount.address,
gasSelector.selectedGasLimit,
gasSelector.eip1599Enabled ? "" : gasSelector.selectedGasPrice,
gasSelector.selectedTipLimit,
gasSelector.selectedOverallLimit,
transactionSigner.enteredPassword)
let response = JSON.parse(responseStr)
// if (!response.success) {
// if (Utils.isInvalidPasswordMessage(response.result)){
// //% "Wrong password"
// transactionSigner.validationError = qsTrId("wrong-password")
// return
// }
// sendingError.text = response.result
// return sendingError.open()
// }
if (!response.success) {
if (Utils.isInvalidPasswordMessage(response.result)){
//% "Wrong password"
transactionSigner.validationError = qsTrId("wrong-password")
return
}
sendingError.text = response.result
return sendingError.open()
}
// onSuccess();
// root.close();
// } catch (e) {
// console.error('Error sending the transaction', e)
// sendingError.text = "Error sending the transaction: " + e.message;
// return sendingError.open()
// }
onSuccess();
root.close();
} catch (e) {
console.error('Error sending the transaction', e)
sendingError.text = "Error sending the transaction: " + e.message;
return sendingError.open()
}
}
property MessageDialog sendingError: MessageDialog {
@ -98,8 +93,7 @@ ModalPopup {
}
return null
}
// Not Refactored Yet
// currency: RootStore.defaultCurrency
currency: root.ensUsernamesStore.getCurrentCurrency()
width: stack.width
//% "Choose account"
label: qsTrId("choose-account")
@ -110,12 +104,10 @@ ModalPopup {
RecipientSelector {
id: selectRecipient
visible: false
// Not Refactored Yet
// accounts: RootStore.walletModelInst.accountsView.accounts
accounts: root.ensUsernamesStore.walletAccounts
contactsStore: root.contactsStore
selectedRecipient: { "address": RootStore.utilsModelInst.ensRegisterAddress, "type": RecipientSelector.Type.Address }
selectedRecipient: { "address": root.ensUsernamesStore.getEnsRegisteredAddress(), "type": RecipientSelector.Type.Address }
readOnly: true
currentIndex: index
onSelectedRecipientChanged: if (isValid) { gasSelector.estimateGas() }
}
GasSelector {
@ -123,11 +115,10 @@ ModalPopup {
visible: true
anchors.top: selectFromAccount.bottom
anchors.topMargin: Style.current.padding
// Not Refactored Yet
// gasPrice: parseFloat(RootStore.gasPrice)
// getGasEthValue: RootStore.gasEthValue
// getFiatValue: RootStore.fiatValue
// defaultCurrency: RootStore.defaultCurrency
gasPrice: parseFloat(root.ensUsernamesStore.gasPrice)
getGasEthValue: root.ensUsernamesStore.getGasEthValue
getFiatValue: root.ensUsernamesStore.getFiatValue
defaultCurrency: root.ensUsernamesStore.getCurrentCurrency()
property var estimateGas: Backpressure.debounce(gasSelector, 600, function() {
let estimatedGas = root.estimateGasFunction(selectFromAccount.selectedAccount);
@ -161,12 +152,11 @@ ModalPopup {
}
toAccount: selectRecipient.selectedRecipient
asset: root.asset
// Not Refactored Yet
// currency: RootStore.defaultCurrency
// amount: {
// const fiatValue = RootStore.walletModelInst.balanceView.getFiatValue(0, root.asset.symbol, currency)
// return { "value": 0, "fiatValue": fiatValue }
// }
currency: root.ensUsernamesStore.getCurrentCurrency()
amount: {
const fiatValue = root.ensUsernamesStore.getFiatValue(0, root.asset.symbol, currency)
return { "value": 0, "fiatValue": fiatValue }
}
}
}
TransactionFormGroup {
@ -178,8 +168,7 @@ ModalPopup {
TransactionSigner {
id: transactionSigner
width: stack.width
// Not Refactored Yet
// signingPhrase: RootStore.walletModelInst.utilsView.signingPhrase
signingPhrase: root.ensUsernamesStore.getSigningPhrase()
}
}
}

View File

@ -15,10 +15,10 @@ import shared.controls 1.0
ModalPopup {
id: root
property var store
property var contactsStore
// Not Refactored Yet
// readonly property var asset: "" //JSON.parse(RootStore.walletModelInst.tokensView.getStatusToken())
readonly property var asset: JSON.parse(root.store.getStatusToken())
property string assetPrice
property string contractAddress
property var estimateGasFunction: (function(userAddress, uuid) { return 0; })
@ -26,8 +26,7 @@ ModalPopup {
property var onSuccess: (function(){})
Component.onCompleted: {
// Not Refactored Yet
// RootStore.walletModelInst.gasView.getGasPrice()
root.store.fetchGasPrice()
}
height: 540
@ -114,23 +113,20 @@ ModalPopup {
RecipientSelector {
id: selectRecipient
visible: false
// Not Refactored Yet
// accounts: RootStore.walletModelInst.accountsView.accounts
accounts: root.store.walletAccounts
contactsStore: root.contactsStore
selectedRecipient: { "address": contractAddress, "type": RecipientSelector.Type.Address }
readOnly: true
currentIndex: index
onSelectedRecipientChanged: if (isValid) { gasSelector.estimateGas() }
}
GasSelector {
id: gasSelector
anchors.top: selectFromAccount.bottom
anchors.topMargin: Style.current.padding
// Not Refactored Yet
// gasPrice: parseFloat(RootStore.gasPrice)
// getGasEthValue: RootStore.gasEthValue
// getFiatValue: RootStore.fiatValue
defaultCurrency: RootStore.currentCurrency
gasPrice: parseFloat(root.store.gasPrice)
getGasEthValue: root.store.getGasEthValue
getFiatValue: root.store.getFiatValue
defaultCurrency: root.store.getCurrentCurrency()
width: stack.width
property var estimateGas: Backpressure.debounce(gasSelector, 600, function() {
@ -166,11 +162,11 @@ ModalPopup {
}
toAccount: selectRecipient.selectedRecipient
asset: root.asset
currency: RootStore.walletSectionInst.currentCurrency
// amount: {
// const fiatValue = RootStore.walletModelInst.balanceView.getFiatValue(root.assetPrice || 0, root.asset.symbol, currency)
// return { "value": root.assetPrice, "fiatValue": fiatValue }
// }
amount: {
const fiatValue = root.store.getFiatValue(root.assetPrice || 0, root.asset.symbol, currency)
return { "value": root.assetPrice, "fiatValue": fiatValue }
}
currency: root.store.getCurrentCurrency()
}
}
TransactionFormGroup {
@ -183,8 +179,7 @@ ModalPopup {
TransactionSigner {
id: transactionSigner
width: stack.width
// Not Refactored Yet
// signingPhrase: RootStore.walletModelInst.utilsView.signingPhrase
signingPhrase: root.store.getSigningPhrase()
}
}
}

View File

@ -313,6 +313,7 @@ Item {
anchors.leftMargin: chatHorizontalPadding
anchors.top: chatImage.top
anchors.left: chatImage.right
messageContextMenu: root.messageContextMenu
displayName: senderDisplayName
localName: senderLocalName
amISender: amISender