feat(@desktop/stickers): support buying/releasing ens usernames if profile keypair is migrated to a keycard

Closes part 2 of #12556
This commit is contained in:
Sale Djenic 2023-11-29 12:46:59 +01:00 committed by saledjenic
parent a057e93901
commit a12f96c311
6 changed files with 380 additions and 306 deletions

View File

@ -1,14 +1,16 @@
import chronicles, json
import Tables, os, uuids, chronicles, json
import io_interface
import ../../../../global/global_singleton
import ../../../../core/eventemitter
import ../../../../../app_service/service/settings/service as settings_service
import ../../../../../app_service/service/ens/service as ens_service
import ../../../../../app_service/service/network/service as network_service
import ../../../../../app_service/service/wallet_account/service as wallet_account_service
import ../../../../../app_service/service/token/service as token_service
import ../../../shared_modules/keycard_popup/io_interface as keycard_shared_module
import app/global/global_singleton
import app/core/eventemitter
import app_service/service/settings/service as settings_service
import app_service/service/ens/service as ens_service
import app_service/service/network/service as network_service
import app_service/service/wallet_account/service as wallet_account_service
import app_service/service/token/service as token_service
import app_service/service/keycard/service as keycard_service
from app_service/service/transaction/dto import PendingTransactionTypeDto
import app/modules/shared_modules/keycard_popup/io_interface as keycard_shared_module
logScope:
topics = "profile-section-ens-usernames-module-controller"
@ -24,12 +26,15 @@ type
networkService: network_service.Service
walletAccountService: wallet_account_service.Service
tokenService: token_service.Service
keycardService: keycard_service.Service
connectionKeycardResponse: UUID
proc newController*(
delegate: io_interface.AccessInterface, events: EventEmitter,
settingsService: settings_service.Service, ensService: ens_service.Service,
walletAccountService: wallet_account_service.Service, networkService: network_service.Service,
tokenService: token_service.Service
tokenService: token_service.Service,
keycardService: keycard_service.Service
): Controller =
result = Controller()
result.delegate = delegate
@ -39,6 +44,7 @@ proc newController*(
result.walletAccountService = walletAccountService
result.networkService = networkService
result.tokenService = tokenService
result.keycardService = keycardService
proc delete*(self: Controller) =
discard
@ -64,7 +70,7 @@ proc init*(self: Controller) =
let args = SharedKeycarModuleArgs(e)
if args.uniqueIdentifier != UNIQUE_ENS_SECTION_TRANSACTION_MODULE_IDENTIFIER:
return
self.delegate.onUserAuthenticated(args.password)
self.delegate.onKeypairAuthenticated(args.password, args.pin)
proc getChainId*(self: Controller): int =
return self.networkService.getChainIdForEns()
@ -87,9 +93,6 @@ proc fetchDetailsForEnsUsername*(self: Controller, chainId: int, ensUsername: st
proc setPubKeyGasEstimate*(self: Controller, chainId: int, ensUsername: string, address: string): int =
return self.ensService.setPubKeyGasEstimate(chainId, ensUsername, address)
proc setPubKey*(self: Controller, chainId: int, ensUsername: string, address: string, gas: string, gasPrice: string,
maxPriorityFeePerGas: string, maxFeePerGas: string, password: string, eip1559Enabled: bool): EnsTxResultArgs =
return self.ensService.setPubKey(chainId, ensUsername, address, gas, gasPrice, maxPriorityFeePerGas, maxFeePerGas, password, eip1559Enabled)
proc getSigningPhrase*(self: Controller): string =
return self.settingsService.getSigningPhrase()
@ -106,10 +109,6 @@ proc getPreferredEnsUsername*(self: Controller): string =
proc releaseEnsEstimate*(self: Controller, chainId: int, ensUsername: string, address: string): int =
return self.ensService.releaseEnsEstimate(chainId, ensUsername, address)
proc release*(self: Controller, chainId: int, ensUsername: string, address: string, gas: string, gasPrice: string,
maxPriorityFeePerGas: string, maxFeePerGas: string, password: string, eip1559Enabled: bool):
EnsTxResultArgs =
return self.ensService.release(chainId, ensUsername, address, gas, gasPrice, maxPriorityFeePerGas, maxFeePerGas, password, eip1559Enabled)
proc setPreferredName*(self: Controller, preferredName: string) =
if(self.settingsService.savePreferredName(preferredName)):
@ -136,9 +135,6 @@ proc getEnsRegisteredAddress*(self: Controller): string =
proc registerEnsGasEstimate*(self: Controller, chainId: int, ensUsername: string, address: string): int =
return self.ensService.registerEnsGasEstimate(chainId, ensUsername, address)
proc registerEns*(self: Controller, chainId: int, ensUsername: string, address: string, gas: string, gasPrice: string,
maxPriorityFeePerGas: string, maxFeePerGas: string, password: string, eip1559Enabled: bool): EnsTxResultArgs =
return self.ensService.registerEns(chainId, ensUsername, address, gas, gasPrice, maxPriorityFeePerGas, maxFeePerGas, password, eip1559Enabled)
proc getSNTBalance*(self: Controller): string =
return self.ensService.getSNTBalance()
@ -146,6 +142,9 @@ proc getSNTBalance*(self: Controller): string =
proc getWalletDefaultAddress*(self: Controller): string =
return self.walletAccountService.getWalletAccount(0).address
proc getKeypairByAccountAddress*(self: Controller, address: string): KeypairDto =
return self.walletAccountService.getKeypairByAccountAddress(address)
proc getCurrentCurrency*(self: Controller): string =
return self.settingsService.getCurrency()
@ -165,7 +164,43 @@ proc getStatusToken*(self: Controller): string =
}
return $jsonObj
proc authenticateUser*(self: Controller, keyUid = "") =
proc authenticate*(self: Controller, keyUid = "") =
let data = SharedKeycarModuleAuthenticationArgs(uniqueIdentifier: UNIQUE_ENS_SECTION_TRANSACTION_MODULE_IDENTIFIER,
keyUid: keyUid)
self.events.emit(SIGNAL_SHARED_KEYCARD_MODULE_AUTHENTICATE_USER, data)
proc prepareEnsTx*(self: Controller, txType: PendingTransactionTypeDto, chainId: int, ensUsername: string, address: string,
gas: string, gasPrice: string, maxPriorityFeePerGas: string, maxFeePerGas: string, eip1559Enabled: bool): JsonNode =
return self.ensService.prepareEnsTx(txType, chainId, ensUsername, address, gas, gasPrice, maxPriorityFeePerGas,
maxFeePerGas, eip1559Enabled)
proc signEnsTxLocally*(self: Controller, data, account, hashedPasssword: string): string =
return self.ensService.signEnsTxLocally(data, account, hashedPasssword)
proc sendEnsTxWithSignatureAndWatch*(self: Controller, txType: PendingTransactionTypeDto, chainId: int,
txData: JsonNode, ensUsername: string, signature: string): EnsTxResultArgs =
return self.ensService.sendEnsTxWithSignatureAndWatch(txType, chainId, txData, ensUsername, signature)
proc disconnectKeycardReponseSignal(self: Controller) =
self.events.disconnect(self.connectionKeycardResponse)
proc connectKeycardReponseSignal(self: Controller) =
self.connectionKeycardResponse = self.events.onWithUUID(SIGNAL_KEYCARD_RESPONSE) do(e: Args):
let args = KeycardLibArgs(e)
self.disconnectKeycardReponseSignal()
let currentFlow = self.keycardService.getCurrentFlow()
if currentFlow != KCSFlowType.Sign:
self.delegate.onTransactionSigned("", KeycardEvent())
return
self.delegate.onTransactionSigned(args.flowType, args.flowEvent)
proc cancelCurrentFlow*(self: Controller) =
self.keycardService.cancelCurrentFlow()
# in most cases we're running another flow after canceling the current one,
# this way we're giving to the keycard some time to cancel the current flow
sleep(200)
proc runSignFlow*(self: Controller, pin, bip44Path, txHash: string) =
self.cancelCurrentFlow()
self.connectKeycardReponseSignal()
self.keycardService.startSignFlow(bip44Path, txHash, pin)

View File

@ -1,4 +1,5 @@
import NimQml
from app_service/service/keycard/service import KeycardEvent
type
AccessInterface* {.pure inheritable.} = ref object of RootObj
@ -100,5 +101,8 @@ method getChainIdForEns*(self: AccessInterface): int {.base.} =
method setPrefferedEnsUsername*(self: AccessInterface, ensUsername: string) {.base.} =
raise newException(ValueError, "No implementation available")
method onUserAuthenticated*(self: AccessInterface, password: string) {.base.} =
method onKeypairAuthenticated*(self: AccessInterface, password: string, pin: string) {.base.} =
raise newException(ValueError, "No implementation available")
method onTransactionSigned*(self: AccessInterface, keycardFlowType: string, keycardEvent: KeycardEvent) {.base.} =
raise newException(ValueError, "No implementation available")

View File

@ -1,25 +1,30 @@
import NimQml, json, stint, strutils, strformat, parseutils, chronicles
import NimQml, json, stint, sequtils, strutils, sugar, strformat, parseutils, chronicles
import io_interface
import ../io_interface as delegate_interface
import view, controller, model
import ../../../../global/global_singleton
import ../../../../core/eventemitter
import ../../../../../app_service/common/conversion as service_conversion
import ../../../../../app_service/service/settings/service as settings_service
import ../../../../../app_service/service/ens/service as ens_service
import ../../../../../app_service/service/network/service as network_service
import ../../../../../app_service/service/ens/utils as ens_utils
import ../../../../../app_service/service/wallet_account/service as wallet_account_service
import ../../../../../app_service/service/token/service as token_service
import app/global/global_singleton
import app/core/eventemitter
import app_service/common/conversion as service_conversion
import app_service/common/utils as common_utils
import app_service/common/wallet_constants as common_wallet_constants
import app_service/service/settings/service as settings_service
import app_service/service/ens/service as ens_service
import app_service/service/network/service as network_service
import app_service/service/ens/utils as ens_utils
import app_service/service/wallet_account/service as wallet_account_service
import app_service/service/token/service as token_service
import app_service/service/keycard/service as keycard_service
import app_service/service/keycard/constants as keycard_constants
from app_service/service/transaction/dto import PendingTransactionTypeDto
export io_interface
logScope:
topics = "profile-section-ens-usernames-module"
include ../../../../../app_service/common/json_utils
include app_service/common/json_utils
const cancelledRequest* = "cancelled"
@ -28,14 +33,14 @@ type TmpSendEnsTransactionDetails = object
chainId: int
ensUsername: string
address: string
addressPath: string
gas: string
gasPrice: string
maxPriorityFeePerGas: string
maxFeePerGas: string
eip1559Enabled: bool
isRegistration: bool
isRelease: bool
isSetPubKey: bool
txType: PendingTransactionTypeDto
txData: JsonNode
type
Module* = ref object of io_interface.AccessInterface
@ -53,12 +58,14 @@ proc newModule*(
walletAccountService: wallet_account_service.Service,
networkService: network_service.Service,
tokenService: token_service.Service,
keycardService: keycard_service.Service
): 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, networkService, tokenService)
result.controller = controller.newController(result, events, settingsService, ensService, walletAccountService,
networkService, tokenService, keycardService)
result.moduleLoaded = false
result.events = events
@ -67,6 +74,13 @@ method delete*(self: Module) =
self.viewVariant.delete
self.controller.delete
proc clear(self: Module) =
self.tmpSendEnsTransactionDetails = TmpSendEnsTransactionDetails()
proc finish(self: Module, chainId: int, txHash: string, error: string) =
self.clear()
self.view.emitTransactionWasSentSignal(chainId, txHash, error)
method load*(self: Module) =
self.controller.init()
@ -122,6 +136,23 @@ method onDetailsForEnsUsername*(self: Module, chainId: int, ensUsername: string,
method setPubKeyGasEstimate*(self: Module, chainId: int, ensUsername: string, address: string): int =
return self.controller.setPubKeyGasEstimate(chainId, ensUsername, address)
# At this moment we're somehow assume that we're sending from the default wallet address. Need to change that!
# This function provides a possibility to authenticate sending from any address, not only from the wallet default one.
proc authenticateKeypairThatContainsObservedAddress*(self: Module) =
if self.tmpSendEnsTransactionDetails.address.len == 0:
error "tehre is no set address"
return
let kp = self.controller.getKeypairByAccountAddress(self.tmpSendEnsTransactionDetails.address)
if kp.migratedToKeycard():
let accounts = kp.accounts.filter(acc => cmpIgnoreCase(acc.address, self.tmpSendEnsTransactionDetails.address) == 0)
if accounts.len != 1:
error "cannot resolve selected account to send from among known keypair accounts"
return
self.tmpSendEnsTransactionDetails.addressPath = accounts[0].path
self.controller.authenticate(kp.keyUid)
else:
self.controller.authenticate()
method authenticateAndSetPubKey*(self: Module, chainId: int, ensUsername: string, address: string, gas: string, gasPrice: string,
maxPriorityFeePerGas: string, maxFeePerGas: string, eip1559Enabled: bool) =
@ -133,38 +164,9 @@ method authenticateAndSetPubKey*(self: Module, chainId: int, ensUsername: string
self.tmpSendEnsTransactionDetails.maxPriorityFeePerGas = maxPriorityFeePerGas
self.tmpSendEnsTransactionDetails.maxFeePerGas = maxFeePerGas
self.tmpSendEnsTransactionDetails.eip1559Enabled = eip1559Enabled
self.tmpSendEnsTransactionDetails.isRegistration = false
self.tmpSendEnsTransactionDetails.isRelease = false
self.tmpSendEnsTransactionDetails.isSetPubKey = true
self.tmpSendEnsTransactionDetails.txType = PendingTransactionTypeDto.SetPubKey
if singletonInstance.userProfile.getIsKeycardUser():
let keyUid = singletonInstance.userProfile.getKeyUid()
self.controller.authenticateUser(keyUid)
else:
self.controller.authenticateUser()
proc setPubKey*(self: Module, password: string) =
let response = self.controller.setPubKey(
self.tmpSendEnsTransactionDetails.chainId,
self.tmpSendEnsTransactionDetails.ensUsername,
self.tmpSendEnsTransactionDetails.address,
self.tmpSendEnsTransactionDetails.gas,
self.tmpSendEnsTransactionDetails.gasPrice,
self.tmpSendEnsTransactionDetails.maxPriorityFeePerGas,
self.tmpSendEnsTransactionDetails.maxFeePerGas,
password,
self.tmpSendEnsTransactionDetails.eip1559Enabled
)
if(not response.error.isEmptyOrWhitespace()):
info "remote call is not executed with success", methodName="setPubKey"
return
let item = Item(chainId: self.tmpSendEnsTransactionDetails.chainId,
ensUsername: self.tmpSendEnsTransactionDetails.ensUsername,
isPending: true)
self.view.model().addItem(item)
self.view.emitTransactionWasSentSignal(response.chainId, response.txHash, response.error)
self.authenticateKeypairThatContainsObservedAddress()
method releaseEnsEstimate*(self: Module, chainId: int, ensUsername: string, address: string): int =
return self.controller.releaseEnsEstimate(chainId, ensUsername, address)
@ -180,15 +182,9 @@ method authenticateAndReleaseEns*(self: Module, chainId: int, ensUsername: strin
self.tmpSendEnsTransactionDetails.maxPriorityFeePerGas = maxPriorityFeePerGas
self.tmpSendEnsTransactionDetails.maxFeePerGas = maxFeePerGas
self.tmpSendEnsTransactionDetails.eip1559Enabled = eip1559Enabled
self.tmpSendEnsTransactionDetails.isRegistration = false
self.tmpSendEnsTransactionDetails.isRelease = true
self.tmpSendEnsTransactionDetails.isSetPubKey = false
self.tmpSendEnsTransactionDetails.txType = PendingTransactionTypeDto.ReleaseENS
if singletonInstance.userProfile.getIsKeycardUser():
let keyUid = singletonInstance.userProfile.getKeyUid()
self.controller.authenticateUser(keyUid)
else:
self.controller.authenticateUser()
self.authenticateKeypairThatContainsObservedAddress()
proc onEnsUsernameRemoved(self: Module, chainId: int, ensUsername: string) =
if (self.controller.getPreferredEnsUsername() == ensUsername):
@ -202,26 +198,6 @@ method removeEnsUsername*(self: Module, chainId: int, ensUsername: string): bool
self.onEnsUsernameRemoved(chainId, ensUsername)
return true
proc releaseEns*(self: Module, password: string) =
let response = self.controller.release(
self.tmpSendEnsTransactionDetails.chainId,
self.tmpSendEnsTransactionDetails.ensUsername,
self.tmpSendEnsTransactionDetails.address,
self.tmpSendEnsTransactionDetails.gas,
self.tmpSendEnsTransactionDetails.gasPrice,
self.tmpSendEnsTransactionDetails.maxPriorityFeePerGas,
self.tmpSendEnsTransactionDetails.maxFeePerGas,
password,
self.tmpSendEnsTransactionDetails.eip1559Enabled
)
if(not response.error.isEmptyOrWhitespace()):
info "remote call is not executed with success", methodName="release"
return
self.onEnsUsernameRemoved(self.tmpSendEnsTransactionDetails.chainId, self.tmpSendEnsTransactionDetails.ensUsername)
self.view.emitTransactionWasSentSignal(response.chainId, response.txHash, response.error)
proc formatUsername(self: Module, ensUsername: string, isStatus: bool): string =
result = ensUsername
if isStatus:
@ -268,59 +244,9 @@ method authenticateAndRegisterEns*(self: Module, chainId: int, ensUsername: stri
self.tmpSendEnsTransactionDetails.maxPriorityFeePerGas = maxPriorityFeePerGas
self.tmpSendEnsTransactionDetails.maxFeePerGas = maxFeePerGas
self.tmpSendEnsTransactionDetails.eip1559Enabled = eip1559Enabled
self.tmpSendEnsTransactionDetails.isRegistration = true
self.tmpSendEnsTransactionDetails.isRelease = false
self.tmpSendEnsTransactionDetails.isSetPubKey = false
self.tmpSendEnsTransactionDetails.txType = PendingTransactionTypeDto.RegisterENS
if singletonInstance.userProfile.getIsKeycardUser():
let keyUid = singletonInstance.userProfile.getKeyUid()
self.controller.authenticateUser(keyUid)
else:
self.controller.authenticateUser()
##################################
## Do Not Delete
##
## Once we start with signing a transactions we shold check if the address we want to send a transaction from is migrated
## or not. In case it's not we should just authenticate logged in user, otherwise we should use one of the keycards that
## address (key pair) is migrated to and sign the transaction using it.
##
## The code bellow is an example how we can achieve that in future, when we start with signing transactions.
##
## let acc = self.controller.getAccountByAddress(from_addr)
## if acc.isNil:
## echo "error: selected account to send a transaction from is not known"
## return
## let keyPair = self.controller.getKeycardsWithSameKeyUid(acc.keyUid)
## if keyPair.len == 0:
## self.controller.authenticateUser()
## else:
## self.controller.authenticateUser(acc.keyUid, acc.path)
##
##################################
proc registerEns(self: Module, password: string) =
let response = self.controller.registerEns(
self.tmpSendEnsTransactionDetails.chainId,
self.tmpSendEnsTransactionDetails.ensUsername,
self.tmpSendEnsTransactionDetails.address,
self.tmpSendEnsTransactionDetails.gas,
self.tmpSendEnsTransactionDetails.gasPrice,
self.tmpSendEnsTransactionDetails.maxPriorityFeePerGas,
self.tmpSendEnsTransactionDetails.maxFeePerGas,
password,
self.tmpSendEnsTransactionDetails.eip1559Enabled
)
if(not response.error.isEmptyOrWhitespace()):
info "remote call is not executed with success", methodName="registerEns"
return
let ensUsername = self.formatUsername(self.tmpSendEnsTransactionDetails.ensUsername, true)
let item = Item(chainId: self.tmpSendEnsTransactionDetails.chainId, ensUsername: ensUsername, isPending: true)
self.controller.fixPreferredName()
self.view.model().addItem(item)
self.view.emitTransactionWasSentSignal(response.chainId, response.txHash, response.error)
self.authenticateKeypairThatContainsObservedAddress()
method getSNTBalance*(self: Module): string =
return self.controller.getSNTBalance()
@ -389,14 +315,104 @@ method getChainIdForEns*(self: Module): int =
method setPrefferedEnsUsername*(self: Module, ensUsername: string) =
self.controller.setPreferredName(ensUsername)
method onUserAuthenticated*(self: Module, password: string) =
if password.len == 0:
self.view.emitTransactionWasSentSignal(chainId = 0, txHash = "", error = cancelledRequest)
else:
if self.tmpSendEnsTransactionDetails.isRegistration:
self.registerEns(password)
elif self.tmpSendEnsTransactionDetails.isRelease:
self.releaseEns(password)
elif self.tmpSendEnsTransactionDetails.isSetPubKey:
self.setPubKey(password)
proc sendEnsTxWithSignatureAndWatch(self: Module, signature: string) =
let response = self.controller.sendEnsTxWithSignatureAndWatch(
self.tmpSendEnsTransactionDetails.txType,
self.tmpSendEnsTransactionDetails.chainId,
self.tmpSendEnsTransactionDetails.txData,
self.tmpSendEnsTransactionDetails.ensUsername,
signature
)
if not response.error.isEmptyOrWhitespace():
error "sending ens tx failed", errMsg=response.error, methodName="sendEnsTxWithSignatureAndWatch"
self.finish(chainId = 0, txHash = "", error = response.error)
return
if self.tmpSendEnsTransactionDetails.txType == PendingTransactionTypeDto.SetPubKey:
let item = Item(chainId: self.tmpSendEnsTransactionDetails.chainId,
ensUsername: self.tmpSendEnsTransactionDetails.ensUsername,
isPending: true)
self.view.model().addItem(item)
elif self.tmpSendEnsTransactionDetails.txType == PendingTransactionTypeDto.ReleaseENS:
self.onEnsUsernameRemoved(self.tmpSendEnsTransactionDetails.chainId, self.tmpSendEnsTransactionDetails.ensUsername)
elif self.tmpSendEnsTransactionDetails.txType == PendingTransactionTypeDto.RegisterENS:
let ensUsername = self.formatUsername(self.tmpSendEnsTransactionDetails.ensUsername, true)
let item = Item(chainId: self.tmpSendEnsTransactionDetails.chainId, ensUsername: ensUsername, isPending: true)
self.controller.fixPreferredName()
self.view.model().addItem(item)
else:
error "unknown ens action", methodName="sendEnsTxWithSignatureAndWatch"
self.finish(chainId = 0, txHash = "", error = "unknown ens action")
return
self.finish(response.chainId, response.txHash, response.error)
method onKeypairAuthenticated*(self: Module, password: string, pin: string) =
if password.len == 0:
self.finish(chainId = 0, txHash = "", error = cancelledRequest)
return
let txDataJson = self.controller.prepareEnsTx(
self.tmpSendEnsTransactionDetails.txType,
self.tmpSendEnsTransactionDetails.chainId,
self.tmpSendEnsTransactionDetails.ensUsername,
self.tmpSendEnsTransactionDetails.address,
self.tmpSendEnsTransactionDetails.gas,
self.tmpSendEnsTransactionDetails.gasPrice,
self.tmpSendEnsTransactionDetails.maxPriorityFeePerGas,
self.tmpSendEnsTransactionDetails.maxFeePerGas,
self.tmpSendEnsTransactionDetails.eip1559Enabled,
)
if txDataJson.isNil or
txDataJson.kind != JsonNodeKind.JObject or
not txDataJson.hasKey("txArgs") or
not txDataJson.hasKey("messageToSign"):
let errMsg = "unexpected response format preparing tx ens username"
error "error", msg=errMsg, methodName="onKeypairAuthenticated"
self.finish(chainId = 0, txHash = "", error = errMsg)
return
var txToBeSigned = txDataJson["messageToSign"].getStr
if txToBeSigned.len != common_wallet_constants.TX_HASH_LEN_WITH_PREFIX:
let errMsg = "unexpected tx hash length"
error "error", msg=errMsg, methodName="onKeypairAuthenticated"
self.finish(chainId = 0, txHash = "", error = errMsg)
return
self.tmpSendEnsTransactionDetails.txData = txDataJson["txArgs"]
if txDataJson.hasKey("signOnKeycard") and txDataJson["signOnKeycard"].getBool:
if pin.len != PINLengthForStatusApp:
let errMsg = "cannot proceed with keycard signing, unexpected pin"
error "error", msg=errMsg, methodName="onKeypairAuthenticated"
self.finish(chainId = 0, txHash = "", error = errMsg)
return
var txForKcFlow = txToBeSigned
if txForKcFlow.startsWith("0x"):
txForKcFlow = txForKcFlow[2..^1]
self.controller.runSignFlow(pin, self.tmpSendEnsTransactionDetails.addressPath, txForKcFlow)
return
var finalPassword = password
if pin.len == 0:
finalPassword = common_utils.hashPassword(password)
let signature = self.controller.signEnsTxLocally(txToBeSigned, self.tmpSendEnsTransactionDetails.address, finalPassword)
if signature.len == 0:
let errMsg = "couldn't sign tx locally"
error "error", msg=errMsg, methodName="onKeypairAuthenticated"
self.finish(chainId = 0, txHash = "", error = errMsg)
return
self.sendEnsTxWithSignatureAndWatch(signature)
method onTransactionSigned*(self: Module, keycardFlowType: string, keycardEvent: KeycardEvent) =
if keycardFlowType != keycard_constants.ResponseTypeValueKeycardFlowResult:
let errMsg = "unexpected error while keycard signing transaction"
error "error", msg=errMsg, methodName="onTransactionSigned"
self.finish(chainId = 0, txHash = "", error = errMsg)
return
let signature = "0x" & keycardEvent.txSignature.r & keycardEvent.txSignature.s & keycardEvent.txSignature.v
self.sendEnsTxWithSignatureAndWatch(signature)

View File

@ -105,7 +105,7 @@ proc newModule*(delegate: delegate_interface.AccessInterface,
result.wakuModule = waku_module.newModule(result, events, settingsService, nodeConfigurationService)
result.notificationsModule = notifications_module.newModule(result, events, settingsService, chatService, contactsService)
result.ensUsernamesModule = ens_usernames_module.newModule(
result, events, settingsService, ensService, walletAccountService, networkService, tokenService
result, events, settingsService, ensService, walletAccountService, networkService, tokenService, keycardService
)
result.communitiesModule = communities_module.newModule(result, communityService)
result.keycardModule = keycard_module.newModule(result, events, keycardService, settingsService, networkService,

View File

@ -1,23 +1,24 @@
import NimQml, Tables, sets, json, sequtils, strutils, chronicles
import web3/ethtypes, stint
import NimQml, Tables, sets, json, sequtils, strutils, stint, chronicles
import web3/ethtypes, web3/conversions, stew/byteutils, nimcrypto, json_serialization
import ../../../app/core/eventemitter
import ../../../app/core/tasks/[qt, threadpool]
import app/core/eventemitter
import app/core/tasks/[qt, threadpool]
import ../../../app/global/global_singleton
import ../../../backend/ens as status_ens
import ../../../backend/backend as status_go_backend
import app/global/global_singleton
import backend/ens as status_ens
import backend/backend as status_go_backend
import backend/wallet as status_wallet
import ../../common/conversion as common_conversion
import ../../common/utils as common_utils
import app_service/common/conversion as common_conversion
import utils as ens_utils
import ../settings/service as settings_service
import ../wallet_account/service as wallet_account_service
import ../transaction/service as transaction_service
import ../network/service as network_service
import ../token/service as token_service
import ../eth/dto/coder
import ../eth/dto/transaction
import app_service/service/settings/service as settings_service
import app_service/service/wallet_account/service as wallet_account_service
import app_service/service/transaction/service as transaction_service
import app_service/service/network/service as network_service
import app_service/service/token/service as token_service
import app_service/service/eth/utils as status_utils
import app_service/service/eth/dto/coder
import app_service/service/eth/dto/transaction
import dto/ens_username_dto
export ens_username_dto
@ -211,7 +212,7 @@ QtObject:
proc checkEnsUsernameAvailability*(self: Service, ensUsername: string, isStatus: bool) =
let registeredEnsUsernames = self.getAllMyEnsUsernames(true)
let dto = EnsUsernameDto(chainId: self.getChainId(),
let dto = EnsUsernameDto(chainId: self.getChainId(),
username: self.formatUsername(ensUsername, isStatus))
var availability = ""
if registeredEnsUsernames.find(dto) >= 0:
@ -267,7 +268,7 @@ QtObject:
proc extractCoordinates(self: Service, pubkey: string):tuple[x: string, y:string] =
result = ("0x" & pubkey[4..67], "0x" & pubkey[68..131])
proc setPubKeyGasEstimate*(self: Service, chainId: int, ensUsername: string, address: string): int =
proc setPubKeyGasEstimate*(self: Service, chainId: int, ensUsername: string, address: string): int =
try:
let txData = ens_utils.buildTransaction(parseAddress(address), 0.u256)
let resp = status_ens.setPubKeyEstimate(chainId, %txData, ensUsername,
@ -277,38 +278,6 @@ QtObject:
result = 80000
error "error occurred", procName="setPubKeyGasEstimate", msg = e.msg
proc setPubKey*(
self: Service,
chainId: int,
ensUsername: string,
address: string,
gas: string,
gasPrice: string,
maxPriorityFeePerGas: string,
maxFeePerGas: string,
password: string,
eip1559Enabled: bool,
): EnsTxResultArgs =
try:
let txData = ens_utils.buildTransaction(parseAddress(address), 0.u256, gas, gasPrice,
eip1559Enabled, maxPriorityFeePerGas, maxFeePerGas)
let resp = status_ens.setPubKey(chainId, %txData, common_utils.hashPassword(password), ensUsername.addDomain(),
singletonInstance.userProfile.getPubKey())
let hash = resp.result.getStr
let resolverAddress = status_ens.resolver(chainId, ensUsername.addDomain()).result.getStr
self.transactionService.watchTransaction(
hash, $address, resolverAddress,
$PendingTransactionTypeDto.SetPubKey, ensUsername, chainId
)
let dto = EnsUsernameDto(chainId: chainId, username: ensUsername)
self.pendingEnsUsernames.incl(dto)
result = EnsTxResultArgs(chainId: chainId, txHash: hash, error: "")
except Exception as e:
error "error occurred", procName="setPubKey", msg = e.msg
result = EnsTxResultArgs(chainId: 0, txHash: "", error: e.msg)
proc releaseEnsEstimate*(self: Service, chainId: int, ensUsername: string, address: string): int =
try:
let txData = ens_utils.buildTransaction(parseAddress(address), 0.u256)
@ -325,43 +294,6 @@ QtObject:
proc getEnsRegisteredAddress*(self: Service): string =
return status_ens.getRegistrarAddress(self.getChainId()).result.getStr
proc release*(
self: Service,
chainId: int,
ensUsername: string,
address: string,
gas: string,
gasPrice: string,
maxPriorityFeePerGas: string,
maxFeePerGas: string,
password: string,
eip1559Enabled: bool
): EnsTxResultArgs =
try:
let
txData = ens_utils.buildTransaction(parseAddress(address), 0.u256, gas, gasPrice,
eip1559Enabled, maxPriorityFeePerGas, maxFeePerGas)
var userNameNoDomain = ensUsername
if ensUsername.endsWith(ens_utils.STATUS_DOMAIN):
userNameNoDomain = ensUsername.replace(ens_utils.STATUS_DOMAIN, "")
let resp = status_ens.release(chainId, %txData, common_utils.hashPassword(password), userNameNoDomain)
let hash = resp.result.getStr
let ensUsernamesAddress = self.getEnsRegisteredAddress()
self.transactionService.watchTransaction(
hash, address, ensUsernamesAddress,
$PendingTransactionTypeDto.ReleaseENS, ensUsername, chainId
)
let dto = EnsUsernameDto(chainId: chainId, username: ensUsername)
self.pendingEnsUsernames.excl(dto)
result = EnsTxResultArgs(chainId: chainId, txHash: hash, error: "")
except Exception as e:
error "error occurred", procName="release", msg = e.msg
result = EnsTxResultArgs(chainId: 0, txHash: "", error: e.msg)
proc registerENSGasEstimate*(self: Service, chainId: int, ensUsername: string, address: string): int =
try:
let txData = ens_utils.buildTransaction(parseAddress(address), 0.u256)
@ -376,39 +308,6 @@ QtObject:
let networkDto = self.networkService.getNetworkForEns()
return self.tokenService.findTokenBySymbol(networkDto.chainId, networkDto.sntSymbol())
proc registerEns*(
self: Service,
chainId: int,
username: string,
address: string,
gas: string,
gasPrice: string,
maxPriorityFeePerGas: string,
maxFeePerGas: string,
password: string,
eip1559Enabled: bool,
): EnsTxResultArgs =
try:
let txData = ens_utils.buildTransaction(parseAddress(address), 0.u256, gas, gasPrice,
eip1559Enabled, maxPriorityFeePerGas, maxFeePerGas)
let resp = status_ens.register(chainId, %txData, common_utils.hashPassword(password), username,
singletonInstance.userProfile.getPubKey())
let hash = resp.result.getStr
let sntContract = self.getStatusToken()
let ensUsername = self.formatUsername(username, true)
self.transactionService.watchTransaction(
hash, address, $sntContract.address,
$PendingTransactionTypeDto.RegisterEns, ensUsername,
chainId
)
let dto = EnsUsernameDto(chainId: chainId, username: ensUsername)
self.pendingEnsUsernames.incl(dto)
result = EnsTxResultArgs(chainId: chainId, txHash: hash, error: "")
except Exception as e:
error "error occurred", procName="registerEns", msg = e.msg
result = EnsTxResultArgs(chainId: 0, txHash: "", error: e.msg)
proc getSNTBalance*(self: Service): string =
let token = self.getStatusToken()
let account = self.walletAccountService.getWalletAccount(0).address
@ -422,3 +321,135 @@ QtObject:
except Exception as e:
error "Error getting ENS resourceUrl", username=username, exception=e.msg
raise
proc prepareEnsTx*(self: Service, txType: PendingTransactionTypeDto, chainId: int, ensUsername: string, address: string,
gas: string, gasPrice: string, maxPriorityFeePerGas: string, maxFeePerGas: string, eip1559Enabled: bool): JsonNode =
try:
var prepareTxResponse: RpcResponse[JsonNode]
let txData = ens_utils.buildTransaction(parseAddress(address), 0.u256, gas, gasPrice, eip1559Enabled,
maxPriorityFeePerGas, maxFeePerGas)
if txType == PendingTransactionTypeDto.SetPubKey:
prepareTxResponse = status_ens.prepareTxForSetPubKey(chainId, %txData, ensUsername.addDomain(),
singletonInstance.userProfile.getPubKey())
elif txType == PendingTransactionTypeDto.RegisterENS:
prepareTxResponse = status_ens.prepareTxForRegisterEnsUsername(chainId, %txData, ensUsername,
singletonInstance.userProfile.getPubKey())
elif txType == PendingTransactionTypeDto.ReleaseENS:
var userNameNoDomain = ensUsername
if ensUsername.endsWith(ens_utils.STATUS_DOMAIN):
userNameNoDomain = ensUsername.replace(ens_utils.STATUS_DOMAIN, "")
prepareTxResponse = status_ens.prepareTxForReleaseEnsUsername(chainId, %txData, userNameNoDomain)
else:
error "Unknown action", procName="prepareEnsTx"
if not prepareTxResponse.error.isNil:
error "error occurred", procName="prepareEnsTx", msg = prepareTxResponse.error.message
return
prepareTxResponse.result["gas"] = %* (if gas.isEmptyOrWhitespace: Quantity.none else: Quantity(cast[uint64](parseFloat(gas).toUInt64)).some)
if eip1559Enabled:
let maxPriorityFeePerGasFinal = if maxPriorityFeePerGas.isEmptyOrWhitespace: Uint256.none else: gwei2Wei(parseFloat(maxPriorityFeePerGas)).some
let maxFeePerGasFinal = if maxFeePerGas.isEmptyOrWhitespace: Uint256.none else: gwei2Wei(parseFloat(maxFeePerGas)).some
prepareTxResponse.result["maxPriorityFeePerGas"] = %* ("0x" & maxPriorityFeePerGasFinal.unsafeGet.toHex)
prepareTxResponse.result["maxFeePerGas"] = %* ("0x" & maxFeePerGasFinal.unsafeGet.toHex)
else:
let gasPriceFinal = if gasPrice.isEmptyOrWhitespace: int.none else: gwei2Wei(parseFloat(gasPrice)).truncate(int).some
prepareTxResponse.result["gasPrice"] = %* ("0x" & gasPriceFinal.unsafeGet.toHex.stripLeadingZeros)
var buildTxResponse: JsonNode
let err = status_wallet.buildTransaction(buildTxResponse, chainId, $prepareTxResponse.result)
if err.len > 0:
error "error occurred", procName="prepareEnsTx", msg = err
return
return buildTxResponse
except Exception as e:
error "error occurred", procName="prepareEnsTx", msg = e.msg
proc signEnsTxLocally*(self: Service, data, account, hashedPasssword: string): string =
try:
var response: JsonNode
let err = status_wallet.signMessage(response, data, account, hashedPasssword)
if err.len > 0 or response.isNil:
error "error occurred", procName="signEnsTxLocally", msg = err
return
return response.getStr()
except Exception as e:
error "error occurred", procName="signEnsTxLocally", msg = e.msg
proc sendEnsTxWithSignatureAndWatch*(self: Service, txType: PendingTransactionTypeDto, chainId: int,
txData: JsonNode, ensUsername: string, signature: string): EnsTxResultArgs =
result = EnsTxResultArgs(chainId: chainId)
try:
if txData.isNil:
result.error = "txData is nil"
error "error occurred", procName="sendEnsTxWithSignatureAndWatch", msg = result.error
return
if not txData.hasKey("from") or txData["from"].getStr().len == 0:
result.error = "from address is empty"
error "error occurred", procName="sendEnsTxWithSignatureAndWatch", msg = result.error
return
if not txData.hasKey("to") or txData["to"].getStr().len == 0:
result.error = "to address is empty"
error "error occurred", procName="sendEnsTxWithSignatureAndWatch", msg = result.error
return
if txType != PendingTransactionTypeDto.SetPubKey and
txType != PendingTransactionTypeDto.RegisterENS and
txType != PendingTransactionTypeDto.ReleaseENS:
result.error = "invalid tx type"
error "error occurred", procName="sendEnsTxWithSignatureAndWatch", msg = result.error
return
var finalSignature = signature
if finalSignature.startsWith("0x"):
finalSignature = finalSignature[2..^1]
var txResponse: JsonNode
let err = status_wallet.sendTransactionWithSignature(txResponse, chainId, $txType, $txData, finalSignature)
if err.len > 0 or txResponse.isNil:
result.error = err
error "error occurred", procName="sendEnsTxWithSignatureAndWatch", msg = result.error
return
let
transactionHash = txResponse.getStr()
fromAddress = txData["from"].getStr()
toAddress = txData["to"].getStr()
if txType == PendingTransactionTypeDto.SetPubKey:
let usernameWithDomain = ensUsername.addDomain()
if not self.add(chainId, usernameWithDomain):
result.error = "failed to add ens username"
error "error occurred", procName="sendEnsTxWithSignatureAndWatch", msg = result.error
return
let resolverAddress = status_ens.resolver(chainId, usernameWithDomain).result.getStr
self.transactionService.watchTransaction(transactionHash, fromAddress, resolverAddress, $txType,
ensUsername, chainId)
let dto = EnsUsernameDto(chainId: chainId, username: ensUsername)
self.pendingEnsUsernames.incl(dto)
elif txType == PendingTransactionTypeDto.RegisterENS:
let sntContract = self.getStatusToken()
let ensUsernameFinal = self.formatUsername(ensUsername, true)
if not self.add(chainId, ensUsernameFinal):
result.error = "failed to add ens username"
error "error occurred", procName="sendEnsTxWithSignatureAndWatch", msg = result.error
return
self.transactionService.watchTransaction(transactionHash, fromAddress, $sntContract.address, $txType,
ensUsernameFinal, chainId)
let dto = EnsUsernameDto(chainId: chainId, username: ensUsernameFinal)
self.pendingEnsUsernames.incl(dto)
elif txType == PendingTransactionTypeDto.ReleaseENS:
let ensUsernameFinal = self.formatUsername(ensUsername, true)
if not self.remove(chainId, ensUsernameFinal):
result.error = "failed to add ens username"
error "error occurred", procName="sendEnsTxWithSignatureAndWatch", msg = result.error
return
let ensUsernamesAddress = self.getEnsRegisteredAddress()
self.transactionService.watchTransaction(transactionHash, fromAddress, ensUsernamesAddress, $txType, ensUsername, chainId)
let dto = EnsUsernameDto(chainId: chainId, username: ensUsername)
self.pendingEnsUsernames.excl(dto)
result.txHash = transactionHash
except Exception as e:
result.error = e.msg
error "error occurred", procName="sendEnsTxWithSignatureAndWatch", msg = result.error

View File

@ -57,38 +57,26 @@ proc resourceURL*(chainId: int, username: string): RpcResponse[JsonNode] {.raise
let payload = %* [chainId, username]
return core.callPrivateRPC("ens_resourceURL", payload)
proc register*(
chainId: int, txData: JsonNode, hashedPassword: string, username: string, pubkey: string
): RpcResponse[JsonNode] {.raises: [Exception].} =
let payload = %* [chainId, txData, hashedPassword, username, pubkey]
return core.callPrivateRPC("ens_register", payload)
proc prepareTxForRegisterEnsUsername*(chainId: int, txData: JsonNode, username: string, pubkey: string): RpcResponse[JsonNode] {.raises: [Exception].} =
let payload = %* [chainId, txData, username, pubkey]
return core.callPrivateRPC("ens_registerPrepareTx", payload)
proc registerEstimate*(
chainId: int, txData: JsonNode, username: string, pubkey: string
): RpcResponse[JsonNode] {.raises: [Exception].} =
proc registerEstimate*(chainId: int, txData: JsonNode, username: string, pubkey: string): RpcResponse[JsonNode] {.raises: [Exception].} =
let payload = %* [chainId, txData, username, pubkey]
return core.callPrivateRPC("ens_registerEstimate", payload)
proc release*(
chainId: int, txData: JsonNode, hashedPassword: string, username: string
): RpcResponse[JsonNode] {.raises: [Exception].} =
let payload = %* [chainId, txData, hashedPassword, username]
return core.callPrivateRPC("ens_release", payload)
proc prepareTxForReleaseEnsUsername*(chainId: int, txData: JsonNode, username: string): RpcResponse[JsonNode] {.raises: [Exception].} =
let payload = %* [chainId, txData, username]
return core.callPrivateRPC("ens_releasePrepareTx", payload)
proc releaseEstimate*(
chainId: int, txData: JsonNode, username: string
): RpcResponse[JsonNode] {.raises: [Exception].} =
proc releaseEstimate*(chainId: int, txData: JsonNode, username: string): RpcResponse[JsonNode] {.raises: [Exception].} =
let payload = %* [chainId, txData, username]
return core.callPrivateRPC("ens_releaseEstimate", payload)
proc setPubKey*(
chainId: int, txData: JsonNode, hashedPassword: string, username: string, pubkey: string
): RpcResponse[JsonNode] {.raises: [Exception].} =
let payload = %* [chainId, txData, hashedPassword, username, pubkey]
return core.callPrivateRPC("ens_setPubKey", payload)
proc setPubKeyEstimate*(
chainId: int, txData: JsonNode, username: string, pubkey: string
): RpcResponse[JsonNode] {.raises: [Exception].} =
proc prepareTxForSetPubKey*(chainId: int, txData: JsonNode, username: string, pubkey: string): RpcResponse[JsonNode] {.raises: [Exception].} =
let payload = %* [chainId, txData, username, pubkey]
return core.callPrivateRPC("ens_setPubKeyEstimate", payload)
return core.callPrivateRPC("ens_setPubKeyPrepareTx", payload)
proc setPubKeyEstimate*(chainId: int, txData: JsonNode, username: string, pubkey: string): RpcResponse[JsonNode] {.raises: [Exception].} =
let payload = %* [chainId, txData, username, pubkey]
return core.callPrivateRPC("ens_setPubKeyEstimate", payload)