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:
parent
b34b9fb347
commit
a1027ff087
|
@ -3,10 +3,13 @@ import io_interface
|
||||||
import ../../../../../app_service/service/transaction/service as transaction_service
|
import ../../../../../app_service/service/transaction/service as transaction_service
|
||||||
import ../../../../../app_service/service/network/service as network_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/wallet_account/service as wallet_account_service
|
||||||
|
import ../../../shared_modules/keycard_popup/io_interface as keycard_shared_module
|
||||||
|
|
||||||
import ../../../../core/[main]
|
import ../../../../core/[main]
|
||||||
import ../../../../core/tasks/[qt, threadpool]
|
import ../../../../core/tasks/[qt, threadpool]
|
||||||
|
|
||||||
|
const UNIQUE_WALLET_SECTION_TRANSACTION_MODULE_IDENTIFIER* = "WalletSection-TransactionModule"
|
||||||
|
|
||||||
type
|
type
|
||||||
Controller* = ref object of RootObj
|
Controller* = ref object of RootObj
|
||||||
delegate: io_interface.AccessInterface
|
delegate: io_interface.AccessInterface
|
||||||
|
@ -69,6 +72,12 @@ proc init*(self: Controller) =
|
||||||
self.events.on(SIGNAL_TRANSACTION_SENT) do(e:Args):
|
self.events.on(SIGNAL_TRANSACTION_SENT) do(e:Args):
|
||||||
self.delegate.transactionWasSent(TransactionSentArgs(e).result)
|
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) =
|
proc checkPendingTransactions*(self: Controller) =
|
||||||
self.transactionService.checkPendingTransactions()
|
self.transactionService.checkPendingTransactions()
|
||||||
|
|
||||||
|
@ -84,6 +93,9 @@ proc getWalletAccount*(self: Controller, accountIndex: int): WalletAccountDto =
|
||||||
proc getAccountByAddress*(self: Controller, address: string): WalletAccountDto =
|
proc getAccountByAddress*(self: Controller, address: string): WalletAccountDto =
|
||||||
self.walletAccountService.getAccountByAddress(address)
|
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) =
|
proc loadTransactions*(self: Controller, address: string, toBlock: Uint256, limit: int = 20, loadMore: bool = false) =
|
||||||
self.transactionService.loadTransactions(address, toBlock, limit, loadMore)
|
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,
|
proc transfer*(self: Controller, from_addr: string, to_addr: string, tokenSymbol: string,
|
||||||
value: string, gas: string, gasPrice: string, maxPriorityFeePerGas: string,maxFeePerGas: string,
|
value: string, gas: string, gasPrice: string, maxPriorityFeePerGas: string,maxFeePerGas: string,
|
||||||
password: string, chainId: string, uuid: string, eip1559Enabled: bool,
|
password: string, chainId: string, uuid: string, eip1559Enabled: bool) =
|
||||||
): bool =
|
discard self.transactionService.transfer(from_addr, to_addr, tokenSymbol, value, gas,
|
||||||
result = self.transactionService.transfer(from_addr, to_addr, tokenSymbol, value, gas,
|
|
||||||
gasPrice, maxPriorityFeePerGas, maxFeePerGas, password, chainId, uuid, eip1559Enabled)
|
gasPrice, maxPriorityFeePerGas, maxFeePerGas, password, chainId, uuid, eip1559Enabled)
|
||||||
|
|
||||||
proc suggestedFees*(self: Controller, chainId: int): string =
|
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 =
|
proc getLastTxBlockNumber*(self: Controller): string =
|
||||||
return self.transactionService.getLastTxBlockNumber(self.networkService.getNetworkForBrowser().chainId)
|
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)
|
|
@ -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.} =
|
method estimateGas*(self: AccessInterface, from_addr: string, to: string, assetSymbol: string, value: string, data: string): string {.base.} =
|
||||||
raise newException(ValueError, "No implementation available")
|
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,
|
tokenSymbol: string, value: string, gas: string, gasPrice: string,
|
||||||
maxPriorityFeePerGas: string, maxFeePerGas: string, password: string,
|
maxPriorityFeePerGas: string, maxFeePerGas: string, chainId: string, uuid: string,
|
||||||
chainId: string, uuid: string, eip1559Enabled: bool): bool {.base.} =
|
eip1559Enabled: bool) {.base.} =
|
||||||
raise newException(ValueError, "No implementation available")
|
raise newException(ValueError, "No implementation available")
|
||||||
|
|
||||||
method transactionWasSent*(self: AccessInterface, result: string) {.base.} =
|
method transactionWasSent*(self: AccessInterface, result: string) {.base.} =
|
||||||
|
|
|
@ -10,12 +10,27 @@ import ../../../../../app_service/service/network/service as network_service
|
||||||
|
|
||||||
export io_interface
|
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
|
type
|
||||||
Module* = ref object of io_interface.AccessInterface
|
Module* = ref object of io_interface.AccessInterface
|
||||||
delegate: delegate_interface.AccessInterface
|
delegate: delegate_interface.AccessInterface
|
||||||
view: View
|
view: View
|
||||||
controller: Controller
|
controller: Controller
|
||||||
moduleLoaded: bool
|
moduleLoaded: bool
|
||||||
|
tmpSendTransactionDetails: TmpSendTransactionDetails
|
||||||
|
|
||||||
# Forward declarations
|
# Forward declarations
|
||||||
method checkRecentHistory*(self: Module)
|
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) =
|
method setIsNonArchivalNode*(self: Module, isNonArchivalNode: bool) =
|
||||||
self.view.setIsNonArchivalNode(isNonArchivalNode)
|
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,
|
value: string, gas: string, gasPrice: string, maxPriorityFeePerGas: string,
|
||||||
maxFeePerGas: string, password: string, chainId: string, uuid: string, eip1559Enabled: bool): bool =
|
maxFeePerGas: string, chainId: string, uuid: string, eip1559Enabled: bool) =
|
||||||
result = self.controller.transfer(from_addr, to_addr, tokenSymbol, value, gas, gasPrice,
|
|
||||||
maxPriorityFeePerGas, maxFeePerGas, password, chainId, uuid, eip1559Enabled)
|
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) =
|
method transactionWasSent*(self: Module, result: string) =
|
||||||
self.view.transactionWasSent(result)
|
self.view.transactionWasSent(result)
|
||||||
|
|
|
@ -116,11 +116,11 @@ QtObject:
|
||||||
proc transactionWasSent*(self: View,txResult: string) {.slot} =
|
proc transactionWasSent*(self: View,txResult: string) {.slot} =
|
||||||
self.transactionSent(txResult)
|
self.transactionSent(txResult)
|
||||||
|
|
||||||
proc transfer*(self: View, from_addr: string, to_addr: string, tokenSymbol: string,
|
proc authenticateAndTransfer*(self: View, from_addr: string, to_addr: string, tokenSymbol: string,
|
||||||
value: string, gas: string, gasPrice: string, maxPriorityFeePerGas: string,
|
value: string, gas: string, gasPrice: string, maxPriorityFeePerGas: string,
|
||||||
maxFeePerGas: string, password: string, chainId: string, uuid: string, eip1559Enabled: bool): bool {.slot.} =
|
maxFeePerGas: string, chainId: string, uuid: string, eip1559Enabled: bool) {.slot.} =
|
||||||
result = self.delegate.transfer(from_addr, to_addr, tokenSymbol, value, gas, gasPrice,
|
self.delegate.authenticateAndTransfer(from_addr, to_addr, tokenSymbol, value, gas, gasPrice,
|
||||||
maxPriorityFeePerGas, maxFeePerGas, password, chainId, uuid, eip1559Enabled)
|
maxPriorityFeePerGas, maxFeePerGas, chainId, uuid, eip1559Enabled)
|
||||||
|
|
||||||
proc suggestedFees*(self: View, chainId: int): string {.slot.} =
|
proc suggestedFees*(self: View, chainId: int): string {.slot.} =
|
||||||
return self.delegate.suggestedFees(chainId)
|
return self.delegate.suggestedFees(chainId)
|
||||||
|
|
|
@ -563,9 +563,9 @@ QtObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
function transfer(from, to, address, tokenSymbol, amount, gasLimit, gasPrice, tipLimit, overallLimit, password, chainId, uuid, eip1559Enabled) {
|
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,
|
from, to, address, tokenSymbol, amount, gasLimit,
|
||||||
gasPrice, tipLimit, overallLimit, password, chainId, uuid,
|
gasPrice, tipLimit, overallLimit, chainId, uuid,
|
||||||
eip1559Enabled
|
eip1559Enabled
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -137,11 +137,10 @@ QtObject {
|
||||||
return profileSectionStore.ensUsernamesStore.getGasEthValue(gweiValue, gasLimit)
|
return profileSectionStore.ensUsernamesStore.getGasEthValue(gweiValue, gasLimit)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function authenticateAndTransfer(from, to, tokenSymbol, amount, gasLimit, gasPrice, tipLimit, overallLimit, chainId, uuid, eip1559Enabled) {
|
||||||
function transfer(from, to, tokenSymbol, amount, gasLimit, gasPrice, tipLimit, overallLimit, password, chainId, uuid, eip1559Enabled) {
|
walletSectionTransactions.authenticateAndTransfer(
|
||||||
return walletSectionTransactions.transfer(
|
|
||||||
from, to, tokenSymbol, amount, gasLimit,
|
from, to, tokenSymbol, amount, gasLimit,
|
||||||
gasPrice, tipLimit, overallLimit, password, chainId, uuid,
|
gasPrice, tipLimit, overallLimit, chainId, uuid,
|
||||||
eip1559Enabled
|
eip1559Enabled
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,9 +38,8 @@ StatusDialog {
|
||||||
|
|
||||||
function sendTransaction() {
|
function sendTransaction() {
|
||||||
let recipientAddress = Utils.isValidAddress(popup.addressText) ? popup.addressText : d.resolvedENSAddress
|
let recipientAddress = Utils.isValidAddress(popup.addressText) ? popup.addressText : d.resolvedENSAddress
|
||||||
let success = false
|
|
||||||
d.isPending = true
|
d.isPending = true
|
||||||
success = popup.store.transfer(
|
popup.store.authenticateAndTransfer(
|
||||||
popup.selectedAccount.address,
|
popup.selectedAccount.address,
|
||||||
recipientAddress,
|
recipientAddress,
|
||||||
assetSelector.selectedAsset.symbol,
|
assetSelector.selectedAsset.symbol,
|
||||||
|
@ -49,7 +48,6 @@ StatusDialog {
|
||||||
gasSelector.suggestedFees.eip1559Enabled ? "" : gasSelector.selectedGasPrice,
|
gasSelector.suggestedFees.eip1559Enabled ? "" : gasSelector.selectedGasPrice,
|
||||||
gasSelector.selectedTipLimit,
|
gasSelector.selectedTipLimit,
|
||||||
gasSelector.selectedOverallLimit,
|
gasSelector.selectedOverallLimit,
|
||||||
transactionSigner.enteredPassword,
|
|
||||||
networkSelector.selectedNetwork.chainId,
|
networkSelector.selectedNetwork.chainId,
|
||||||
d.uuid,
|
d.uuid,
|
||||||
gasSelector.suggestedFees.eip1559Enabled,
|
gasSelector.suggestedFees.eip1559Enabled,
|
||||||
|
@ -73,8 +71,7 @@ StatusDialog {
|
||||||
})
|
})
|
||||||
|
|
||||||
enum StackGroup {
|
enum StackGroup {
|
||||||
SendDetailsGroup = 0,
|
SendDetailsGroup = 0
|
||||||
AuthenticationGroup = 1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QtObject {
|
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 {
|
footer: SendModalFooter {
|
||||||
|
@ -506,10 +490,6 @@ StatusDialog {
|
||||||
isLastGroup: d.isLastGroup
|
isLastGroup: d.isLastGroup
|
||||||
visible: d.isReady && !isNaN(parseFloat(amountToSendInput.text)) && gasValidator.isValid
|
visible: d.isReady && !isNaN(parseFloat(amountToSendInput.text)) && gasValidator.isValid
|
||||||
onNextButtonClicked: {
|
onNextButtonClicked: {
|
||||||
if (isLastGroup) {
|
|
||||||
return popup.sendTransaction()
|
|
||||||
}
|
|
||||||
|
|
||||||
if(gasSelector.suggestedFees.eip1559Enabled && gasSelector.advancedMode){
|
if(gasSelector.suggestedFees.eip1559Enabled && gasSelector.advancedMode){
|
||||||
if(gasSelector.showPriceLimitWarning || gasSelector.showTipLimitWarning){
|
if(gasSelector.showPriceLimitWarning || gasSelector.showTipLimitWarning){
|
||||||
Global.openPopup(transactionSettingsConfirmationPopupComponent, {
|
Global.openPopup(transactionSettingsConfirmationPopupComponent, {
|
||||||
|
@ -523,13 +503,18 @@ StatusDialog {
|
||||||
showPriceLimitWarning: gasSelector.showPriceLimitWarning,
|
showPriceLimitWarning: gasSelector.showPriceLimitWarning,
|
||||||
showTipLimitWarning: gasSelector.showTipLimitWarning,
|
showTipLimitWarning: gasSelector.showTipLimitWarning,
|
||||||
onConfirm: function(){
|
onConfirm: function(){
|
||||||
stack.currentIndex = SendModal.StackGroup.AuthenticationGroup
|
if (isLastGroup) {
|
||||||
|
return popup.sendTransaction()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stack.currentIndex = SendModal.StackGroup.AuthenticationGroup
|
|
||||||
|
if (isLastGroup) {
|
||||||
|
return popup.sendTransaction()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -547,10 +532,6 @@ StatusDialog {
|
||||||
if (response.uuid !== d.uuid) return
|
if (response.uuid !== d.uuid) return
|
||||||
|
|
||||||
if (!response.success) {
|
if (!response.success) {
|
||||||
if (Utils.isInvalidPasswordMessage(response.result)){
|
|
||||||
transactionSigner.validationError = qsTr("Wrong password")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
sendingError.text = response.result
|
sendingError.text = response.result
|
||||||
return sendingError.open()
|
return sendingError.open()
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue