feat(@desktop/keycard): sign transaction using `Authenticate` flow

Actually this is not a signing transaction, but rather authenticating logged in
user when he wants to send a transaction. An authentication is done by entering
password(regular user) or pin(keycard user).

A real signing transaction feature will be (hopefully) added in a near future where
we're going to sign a transaction on a keycard which corresponds to a certain
account, a user wants to send a transaction from.

To sum up... this change just removes password from the send modal and introduces
`Authenticate` flow instead.

Fixes: #7510
This commit is contained in:
Sale Djenic 2022-09-28 10:43:37 +02:00 committed by saledjenic
parent b34b9fb347
commit a1027ff087
7 changed files with 110 additions and 49 deletions

View File

@ -3,10 +3,13 @@ import io_interface
import ../../../../../app_service/service/transaction/service as transaction_service
import ../../../../../app_service/service/network/service as network_service
import ../../../../../app_service/service/wallet_account/service as wallet_account_service
import ../../../shared_modules/keycard_popup/io_interface as keycard_shared_module
import ../../../../core/[main]
import ../../../../core/tasks/[qt, threadpool]
const UNIQUE_WALLET_SECTION_TRANSACTION_MODULE_IDENTIFIER* = "WalletSection-TransactionModule"
type
Controller* = ref object of RootObj
delegate: io_interface.AccessInterface
@ -69,6 +72,12 @@ proc init*(self: Controller) =
self.events.on(SIGNAL_TRANSACTION_SENT) do(e:Args):
self.delegate.transactionWasSent(TransactionSentArgs(e).result)
self.events.on(SIGNAL_SHARED_KEYCARD_MODULE_USER_AUTHENTICATED) do(e: Args):
let args = SharedKeycarModuleArgs(e)
if args.uniqueIdentifier != UNIQUE_WALLET_SECTION_TRANSACTION_MODULE_IDENTIFIER:
return
self.delegate.onUserAuthenticated(args.password)
proc checkPendingTransactions*(self: Controller) =
self.transactionService.checkPendingTransactions()
@ -84,6 +93,9 @@ proc getWalletAccount*(self: Controller, accountIndex: int): WalletAccountDto =
proc getAccountByAddress*(self: Controller, address: string): WalletAccountDto =
self.walletAccountService.getAccountByAddress(address)
proc getMigratedKeyPairByKeyUid*(self: Controller, keyUid: string): seq[KeyPairDto] =
return self.walletAccountService.getMigratedKeyPairByKeyUid(keyUid)
proc loadTransactions*(self: Controller, address: string, toBlock: Uint256, limit: int = 20, loadMore: bool = false) =
self.transactionService.loadTransactions(address, toBlock, limit, loadMore)
@ -95,9 +107,8 @@ proc estimateGas*(self: Controller, from_addr: string, to: string, assetSymbol:
proc transfer*(self: Controller, from_addr: string, to_addr: string, tokenSymbol: string,
value: string, gas: string, gasPrice: string, maxPriorityFeePerGas: string,maxFeePerGas: string,
password: string, chainId: string, uuid: string, eip1559Enabled: bool,
): bool =
result = self.transactionService.transfer(from_addr, to_addr, tokenSymbol, value, gas,
password: string, chainId: string, uuid: string, eip1559Enabled: bool) =
discard self.transactionService.transfer(from_addr, to_addr, tokenSymbol, value, gas,
gasPrice, maxPriorityFeePerGas, maxFeePerGas, password, chainId, uuid, eip1559Enabled)
proc suggestedFees*(self: Controller, chainId: int): string =
@ -119,3 +130,11 @@ proc getEstimatedTime*(self: Controller, chainId: int, maxFeePerGas: string): Es
proc getLastTxBlockNumber*(self: Controller): string =
return self.transactionService.getLastTxBlockNumber(self.networkService.getNetworkForBrowser().chainId)
proc authenticateUser*(self: Controller, keyUid = "", bip44Path = "", txHash = "") =
let data = SharedKeycarModuleAuthenticationArgs(uniqueIdentifier: UNIQUE_WALLET_SECTION_TRANSACTION_MODULE_IDENTIFIER,
keyUid: keyUid,
bip44Path: bip44Path,
txHash: txHash)
self.events.emit(SIGNAL_SHARED_KEYCARD_MODULE_AUTHENTICATE_USER, data)

View File

@ -43,10 +43,13 @@ method setIsNonArchivalNode*(self: AccessInterface, isNonArchivalNode: bool) {.b
method estimateGas*(self: AccessInterface, from_addr: string, to: string, assetSymbol: string, value: string, data: string): string {.base.} =
raise newException(ValueError, "No implementation available")
method transfer*(self: AccessInterface, from_addr: string, to_addr: string,
method onUserAuthenticated*(self: AccessInterface, password: string) {.base.} =
raise newException(ValueError, "No implementation available")
method authenticateAndTransfer*(self: AccessInterface, from_addr: string, to_addr: string,
tokenSymbol: string, value: string, gas: string, gasPrice: string,
maxPriorityFeePerGas: string, maxFeePerGas: string, password: string,
chainId: string, uuid: string, eip1559Enabled: bool): bool {.base.} =
maxPriorityFeePerGas: string, maxFeePerGas: string, chainId: string, uuid: string,
eip1559Enabled: bool) {.base.} =
raise newException(ValueError, "No implementation available")
method transactionWasSent*(self: AccessInterface, result: string) {.base.} =

View File

@ -10,12 +10,27 @@ import ../../../../../app_service/service/network/service as network_service
export io_interface
# Shouldn't be public ever, user only within this module.
type TmpSendTransactionDetails = object
fromAddr: string
toAddr: string
tokenSymbol: string
value: string
gas: string
gasPrice: string
maxPriorityFeePerGas: string
maxFeePerGas: string
chainId: string
uuid: string
eip1559Enabled: bool
type
Module* = ref object of io_interface.AccessInterface
delegate: delegate_interface.AccessInterface
view: View
controller: Controller
moduleLoaded: bool
tmpSendTransactionDetails: TmpSendTransactionDetails
# Forward declarations
method checkRecentHistory*(self: Module)
@ -90,11 +105,55 @@ method estimateGas*(self: Module, from_addr: string, to: string, assetSymbol: st
method setIsNonArchivalNode*(self: Module, isNonArchivalNode: bool) =
self.view.setIsNonArchivalNode(isNonArchivalNode)
method transfer*(self: Module, from_addr: string, to_addr: string, tokenSymbol: string,
method authenticateAndTransfer*(self: Module, from_addr: string, to_addr: string, tokenSymbol: string,
value: string, gas: string, gasPrice: string, maxPriorityFeePerGas: string,
maxFeePerGas: string, password: string, chainId: string, uuid: string, eip1559Enabled: bool): bool =
result = self.controller.transfer(from_addr, to_addr, tokenSymbol, value, gas, gasPrice,
maxPriorityFeePerGas, maxFeePerGas, password, chainId, uuid, eip1559Enabled)
maxFeePerGas: string, chainId: string, uuid: string, eip1559Enabled: bool) =
self.tmpSendTransactionDetails.fromAddr = from_addr
self.tmpSendTransactionDetails.toAddr = to_addr
self.tmpSendTransactionDetails.tokenSymbol = tokenSymbol
self.tmpSendTransactionDetails.value = value
self.tmpSendTransactionDetails.gas = gas
self.tmpSendTransactionDetails.gasPrice = gasPrice
self.tmpSendTransactionDetails.maxPriorityFeePerGas = maxPriorityFeePerGas
self.tmpSendTransactionDetails.maxFeePerGas = maxFeePerGas
self.tmpSendTransactionDetails.chainId = chainId
self.tmpSendTransactionDetails.uuid = uuid
self.tmpSendTransactionDetails.eip1559Enabled = eip1559Enabled
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.getMigratedKeyPairByKeyUid(acc.keyUid)
## if keyPair.len == 0:
## self.controller.authenticateUser()
## else:
## self.controller.authenticateUser(acc.keyUid, acc.path)
##
##################################
method onUserAuthenticated*(self: Module, password: string) =
self.controller.transfer(self.tmpSendTransactionDetails.fromAddr, self.tmpSendTransactionDetails.toAddr,
self.tmpSendTransactionDetails.tokenSymbol, self.tmpSendTransactionDetails.value, self.tmpSendTransactionDetails.gas,
self.tmpSendTransactionDetails.gasPrice, self.tmpSendTransactionDetails.maxPriorityFeePerGas,
self.tmpSendTransactionDetails.maxFeePerGas, password, self.tmpSendTransactionDetails.chainId, self.tmpSendTransactionDetails.uuid,
self.tmpSendTransactionDetails.eip1559Enabled)
method transactionWasSent*(self: Module, result: string) =
self.view.transactionWasSent(result)

View File

@ -116,11 +116,11 @@ QtObject:
proc transactionWasSent*(self: View,txResult: string) {.slot} =
self.transactionSent(txResult)
proc transfer*(self: View, from_addr: string, to_addr: string, tokenSymbol: string,
value: string, gas: string, gasPrice: string, maxPriorityFeePerGas: string,
maxFeePerGas: string, password: string, chainId: string, uuid: string, eip1559Enabled: bool): bool {.slot.} =
result = self.delegate.transfer(from_addr, to_addr, tokenSymbol, value, gas, gasPrice,
maxPriorityFeePerGas, maxFeePerGas, password, chainId, uuid, eip1559Enabled)
proc authenticateAndTransfer*(self: View, from_addr: string, to_addr: string, tokenSymbol: string,
value: string, gas: string, gasPrice: string, maxPriorityFeePerGas: string,
maxFeePerGas: string, chainId: string, uuid: string, eip1559Enabled: bool) {.slot.} =
self.delegate.authenticateAndTransfer(from_addr, to_addr, tokenSymbol, value, gas, gasPrice,
maxPriorityFeePerGas, maxFeePerGas, chainId, uuid, eip1559Enabled)
proc suggestedFees*(self: View, chainId: int): string {.slot.} =
return self.delegate.suggestedFees(chainId)

View File

@ -563,9 +563,9 @@ QtObject {
}
function transfer(from, to, address, tokenSymbol, amount, gasLimit, gasPrice, tipLimit, overallLimit, password, chainId, uuid, eip1559Enabled) {
return walletSectionTransactions.transfer(
return walletSectionTransactions.authenticateAndTransfer(
from, to, address, tokenSymbol, amount, gasLimit,
gasPrice, tipLimit, overallLimit, password, chainId, uuid,
gasPrice, tipLimit, overallLimit, chainId, uuid,
eip1559Enabled
);
}

View File

@ -137,11 +137,10 @@ QtObject {
return profileSectionStore.ensUsernamesStore.getGasEthValue(gweiValue, gasLimit)
}
function transfer(from, to, tokenSymbol, amount, gasLimit, gasPrice, tipLimit, overallLimit, password, chainId, uuid, eip1559Enabled) {
return walletSectionTransactions.transfer(
function authenticateAndTransfer(from, to, tokenSymbol, amount, gasLimit, gasPrice, tipLimit, overallLimit, chainId, uuid, eip1559Enabled) {
walletSectionTransactions.authenticateAndTransfer(
from, to, tokenSymbol, amount, gasLimit,
gasPrice, tipLimit, overallLimit, password, chainId, uuid,
gasPrice, tipLimit, overallLimit, chainId, uuid,
eip1559Enabled
);
}

View File

@ -38,9 +38,8 @@ StatusDialog {
function sendTransaction() {
let recipientAddress = Utils.isValidAddress(popup.addressText) ? popup.addressText : d.resolvedENSAddress
let success = false
d.isPending = true
success = popup.store.transfer(
popup.store.authenticateAndTransfer(
popup.selectedAccount.address,
recipientAddress,
assetSelector.selectedAsset.symbol,
@ -49,7 +48,6 @@ StatusDialog {
gasSelector.suggestedFees.eip1559Enabled ? "" : gasSelector.selectedGasPrice,
gasSelector.selectedTipLimit,
gasSelector.selectedOverallLimit,
transactionSigner.enteredPassword,
networkSelector.selectedNetwork.chainId,
d.uuid,
gasSelector.suggestedFees.eip1559Enabled,
@ -73,8 +71,7 @@ StatusDialog {
})
enum StackGroup {
SendDetailsGroup = 0,
AuthenticationGroup = 1
SendDetailsGroup = 0
}
QtObject {
@ -484,19 +481,6 @@ StatusDialog {
}
}
}
Column{
id: group2
Layout.preferredWidth: parent.width
TransactionSigner {
id: transactionSigner
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: Style.current.smallPadding
anchors.margins: 32
signingPhrase: popup.store.signingPhrase
}
}
}
footer: SendModalFooter {
@ -506,10 +490,6 @@ StatusDialog {
isLastGroup: d.isLastGroup
visible: d.isReady && !isNaN(parseFloat(amountToSendInput.text)) && gasValidator.isValid
onNextButtonClicked: {
if (isLastGroup) {
return popup.sendTransaction()
}
if(gasSelector.suggestedFees.eip1559Enabled && gasSelector.advancedMode){
if(gasSelector.showPriceLimitWarning || gasSelector.showTipLimitWarning){
Global.openPopup(transactionSettingsConfirmationPopupComponent, {
@ -523,13 +503,18 @@ StatusDialog {
showPriceLimitWarning: gasSelector.showPriceLimitWarning,
showTipLimitWarning: gasSelector.showTipLimitWarning,
onConfirm: function(){
stack.currentIndex = SendModal.StackGroup.AuthenticationGroup
if (isLastGroup) {
return popup.sendTransaction()
}
}
})
return
}
}
stack.currentIndex = SendModal.StackGroup.AuthenticationGroup
if (isLastGroup) {
return popup.sendTransaction()
}
}
}
@ -547,10 +532,6 @@ StatusDialog {
if (response.uuid !== d.uuid) return
if (!response.success) {
if (Utils.isInvalidPasswordMessage(response.result)){
transactionSigner.validationError = qsTr("Wrong password")
return
}
sendingError.text = response.result
return sendingError.open()
}