chore: sign the flow only once if approval and transaction need to be placed once after another (swap flow)

Closes #16337
This commit is contained in:
Sale Djenic 2024-10-08 17:28:33 +02:00 committed by saledjenic
parent 684d0b4d63
commit fe5c135486
10 changed files with 60 additions and 56 deletions

View File

@ -44,7 +44,7 @@ method stopUpdatesForSuggestedRoute*(self: AccessInterface) {.base.} =
method suggestedRoutesReady*(self: AccessInterface, uuid: string, suggestedRoutes: SuggestedRoutesDto, errCode: string, errDescription: string) {.base.} = method suggestedRoutesReady*(self: AccessInterface, uuid: string, suggestedRoutes: SuggestedRoutesDto, errCode: string, errDescription: string) {.base.} =
raise newException(ValueError, "No implementation available") raise newException(ValueError, "No implementation available")
method authenticateAndTransferV2*(self: AccessInterface, fromAddr: string, uuid: string, slippagePercentage: float) {.base.} = method authenticateAndTransfer*(self: AccessInterface, fromAddr: string, uuid: string, slippagePercentage: float) {.base.} =
raise newException(ValueError, "No implementation available") raise newException(ValueError, "No implementation available")
method onUserAuthenticated*(self: AccessInterface, password: string, pin: string) {.base.} = method onUserAuthenticated*(self: AccessInterface, password: string, pin: string) {.base.} =

View File

@ -26,17 +26,13 @@ const authenticationCanceled* = "authenticationCanceled"
# Shouldn't be public ever, use only within this module. # Shouldn't be public ever, use only within this module.
type TmpSendTransactionDetails = object type TmpSendTransactionDetails = object
fromAddr: string
fromAddrPath: string fromAddrPath: string
toAddr: string
assetKey: string
toAssetKey: string
paths: seq[TransactionPathDto]
uuid: string uuid: string
sendType: SendType sendType: SendType
pin: string
password: string
txHashBeingProcessed: string
resolvedSignatures: TransactionsSignatures resolvedSignatures: TransactionsSignatures
tokenName: string
isOwnerToken: bool
slippagePercentage: float slippagePercentage: float
type type
@ -48,9 +44,7 @@ type
controller: controller.Controller controller: controller.Controller
moduleLoaded: bool moduleLoaded: bool
tmpSendTransactionDetails: TmpSendTransactionDetails tmpSendTransactionDetails: TmpSendTransactionDetails
tmpPin: string tmpKeepPinPass: bool
tmpPassword: string
tmpTxHashBeingProcessed: string
# Forward declaration # Forward declaration
method getTokenBalance*(self: Module, address: string, chainId: int, tokensKey: string): CurrencyAmount method getTokenBalance*(self: Module, address: string, chainId: int, tokensKey: string): CurrencyAmount
@ -79,12 +73,15 @@ method delete*(self: Module) =
self.view.delete self.view.delete
self.controller.delete self.controller.delete
proc clearTmpData(self: Module) = proc clearTmpData(self: Module, keepPinPass = false) =
self.tmpPin = "" if keepPinPass:
self.tmpPassword = "" self.tmpSendTransactionDetails = TmpSendTransactionDetails(
self.tmpTxHashBeingProcessed = "" sendType: self.tmpSendTransactionDetails.sendType,
pin: self.tmpSendTransactionDetails.pin,
password: self.tmpSendTransactionDetails.password
)
return
self.tmpSendTransactionDetails = TmpSendTransactionDetails() self.tmpSendTransactionDetails = TmpSendTransactionDetails()
writeStackTrace()
proc convertSendToNetworkToNetworkItem(self: Module, network: SendToNetwork): NetworkRouteItem = proc convertSendToNetworkToNetworkItem(self: Module, network: SendToNetwork): NetworkRouteItem =
result = initNetworkRouteItem( result = initNetworkRouteItem(
@ -183,11 +180,22 @@ method getNetworkItem*(self: Module, chainId: int): network_service_item.Network
return nil return nil
return networks[0] return networks[0]
method authenticateAndTransferV2*(self: Module, fromAddr: string, uuid: string, slippagePercentage: float) = proc buildTransactionsFromRoute(self: Module) =
let err = self.controller.buildTransactionsFromRoute(self.tmpSendTransactionDetails.uuid, self.tmpSendTransactionDetails.slippagePercentage)
if err.len > 0:
self.transactionWasSent(uuid = self.tmpSendTransactionDetails.uuid, chainId = 0, approvalTx = false, txHash = "", error = err)
self.clearTmpData()
method authenticateAndTransfer*(self: Module, fromAddr: string, uuid: string, slippagePercentage: float) =
self.tmpSendTransactionDetails.uuid = uuid self.tmpSendTransactionDetails.uuid = uuid
self.tmpSendTransactionDetails.slippagePercentage = slippagePercentage self.tmpSendTransactionDetails.slippagePercentage = slippagePercentage
self.tmpSendTransactionDetails.resolvedSignatures.clear() self.tmpSendTransactionDetails.resolvedSignatures.clear()
if self.tmpKeepPinPass:
# no need to authenticate again, just send a swap tx
self.buildTransactionsFromRoute()
return
let kp = self.controller.getKeypairByAccountAddress(fromAddr) let kp = self.controller.getKeypairByAccountAddress(fromAddr)
if kp.migratedToKeycard(): if kp.migratedToKeycard():
let accounts = kp.accounts.filter(acc => cmpIgnoreCase(acc.address, fromAddr) == 0) let accounts = kp.accounts.filter(acc => cmpIgnoreCase(acc.address, fromAddr) == 0)
@ -203,12 +211,9 @@ method onUserAuthenticated*(self: Module, password: string, pin: string) =
self.transactionWasSent(uuid = self.tmpSendTransactionDetails.uuid, chainId = 0, approvalTx = false, txHash = "", error = authenticationCanceled) self.transactionWasSent(uuid = self.tmpSendTransactionDetails.uuid, chainId = 0, approvalTx = false, txHash = "", error = authenticationCanceled)
self.clearTmpData() self.clearTmpData()
else: else:
self.tmpPin = pin self.tmpSendTransactionDetails.pin = pin
self.tmpPassword = password self.tmpSendTransactionDetails.password = password
let err = self.controller.buildTransactionsFromRoute(self.tmpSendTransactionDetails.uuid, self.tmpSendTransactionDetails.slippagePercentage) self.buildTransactionsFromRoute()
if err.len > 0:
self.transactionWasSent(uuid = self.tmpSendTransactionDetails.uuid, chainId = 0, approvalTx = false, txHash = "", error = err)
self.clearTmpData()
proc sendSignedTransactions*(self: Module) = proc sendSignedTransactions*(self: Module) =
try: try:
@ -226,17 +231,17 @@ proc sendSignedTransactions*(self: Module) =
self.clearTmpData() self.clearTmpData()
proc signOnKeycard(self: Module) = proc signOnKeycard(self: Module) =
self.tmpTxHashBeingProcessed = "" self.tmpSendTransactionDetails.txHashBeingProcessed = ""
for h, (r, s, v) in self.tmpSendTransactionDetails.resolvedSignatures.pairs: for h, (r, s, v) in self.tmpSendTransactionDetails.resolvedSignatures.pairs:
if r.len != 0 and s.len != 0 and v.len != 0: if r.len != 0 and s.len != 0 and v.len != 0:
continue continue
self.tmpTxHashBeingProcessed = h self.tmpSendTransactionDetails.txHashBeingProcessed = h
var txForKcFlow = self.tmpTxHashBeingProcessed var txForKcFlow = self.tmpSendTransactionDetails.txHashBeingProcessed
if txForKcFlow.startsWith("0x"): if txForKcFlow.startsWith("0x"):
txForKcFlow = txForKcFlow[2..^1] txForKcFlow = txForKcFlow[2..^1]
self.controller.runSignFlow(self.tmpPin, self.tmpSendTransactionDetails.fromAddrPath, txForKcFlow) self.controller.runSignFlow(self.tmpSendTransactionDetails.pin, self.tmpSendTransactionDetails.fromAddrPath, txForKcFlow)
break break
if self.tmpTxHashBeingProcessed.len == 0: if self.tmpSendTransactionDetails.txHashBeingProcessed.len == 0:
self.sendSignedTransactions() self.sendSignedTransactions()
self.clearTmpData() self.clearTmpData()
@ -265,7 +270,7 @@ method prepareSignaturesForTransactions*(self:Module, txForSigning: RouterTransa
self.tmpSendTransactionDetails.resolvedSignatures[h] = ("", "", "") self.tmpSendTransactionDetails.resolvedSignatures[h] = ("", "", "")
self.signOnKeycard() self.signOnKeycard()
else: else:
let finalPassword = hashPassword(self.tmpPassword) let finalPassword = hashPassword(self.tmpSendTransactionDetails.password)
for h in txForSigning.signingDetails.hashes: for h in txForSigning.signingDetails.hashes:
self.tmpSendTransactionDetails.resolvedSignatures[h] = ("", "", "") self.tmpSendTransactionDetails.resolvedSignatures[h] = ("", "", "")
var var
@ -288,19 +293,18 @@ method onTransactionSigned*(self: Module, keycardFlowType: string, keycardEvent:
self.transactionWasSent(uuid = self.tmpSendTransactionDetails.uuid, chainId = 0, approvalTx = false, txHash = "", error = err) self.transactionWasSent(uuid = self.tmpSendTransactionDetails.uuid, chainId = 0, approvalTx = false, txHash = "", error = err)
self.clearTmpData() self.clearTmpData()
return return
self.tmpSendTransactionDetails.resolvedSignatures[self.tmpTxHashBeingProcessed] = (keycardEvent.txSignature.r, self.tmpSendTransactionDetails.resolvedSignatures[self.tmpSendTransactionDetails.txHashBeingProcessed] = (keycardEvent.txSignature.r,
keycardEvent.txSignature.s, keycardEvent.txSignature.v) keycardEvent.txSignature.s, keycardEvent.txSignature.v)
self.signOnKeycard() self.signOnKeycard()
method transactionWasSent*(self: Module, uuid: string, chainId: int = 0, approvalTx: bool = false, txHash: string = "", error: string = "") = method transactionWasSent*(self: Module, uuid: string, chainId: int = 0, approvalTx: bool = false, txHash: string = "", error: string = "") =
self.tmpKeepPinPass = approvalTx # need to automate the swap flow with approval
if txHash.len == 0: if txHash.len == 0:
self.view.sendTransactionSentSignal(uuid = self.tmpSendTransactionDetails.uuid, chainId = 0, approvalTx = false, txHash = "", error) self.view.sendTransactionSentSignal(uuid = self.tmpSendTransactionDetails.uuid, chainId = 0, approvalTx = false, txHash = "", error)
return return
self.view.sendTransactionSentSignal(uuid, chainId, approvalTx, txHash, error) self.view.sendTransactionSentSignal(uuid, chainId, approvalTx, txHash, error)
method suggestedRoutesReady*(self: Module, uuid: string, suggestedRoutes: SuggestedRoutesDto, errCode: string, errDescription: string) = method suggestedRoutesReady*(self: Module, uuid: string, suggestedRoutes: SuggestedRoutesDto, errCode: string, errDescription: string) =
self.tmpSendTransactionDetails.paths = suggestedRoutes.best
self.tmpSendTransactionDetails.slippagePercentage = 0
let paths = suggestedRoutes.best.map(x => self.convertTransactionPathDtoToSuggestedRouteItem(x)) let paths = suggestedRoutes.best.map(x => self.convertTransactionPathDtoToSuggestedRouteItem(x))
let suggestedRouteModel = newSuggestedRouteModel() let suggestedRouteModel = newSuggestedRouteModel()
suggestedRouteModel.setItems(paths) suggestedRouteModel.setItems(paths)
@ -332,6 +336,7 @@ method suggestedRoutes*(self: Module,
disabledToChainIDs: seq[int] = @[], disabledToChainIDs: seq[int] = @[],
lockedInAmounts: Table[string, string] = initTable[string, string](), lockedInAmounts: Table[string, string] = initTable[string, string](),
extraParamsTable: Table[string, string] = initTable[string, string]()) = extraParamsTable: Table[string, string] = initTable[string, string]()) =
self.tmpSendTransactionDetails.sendType = sendType
self.controller.suggestedRoutes( self.controller.suggestedRoutes(
uuid, uuid,
sendType, sendType,
@ -403,4 +408,5 @@ method splitAndFormatAddressPrefix*(self: Module, text : string, updateInStore:
return editedText return editedText
method transactionSendingComplete*(self: Module, txHash: string, status: string) = method transactionSendingComplete*(self: Module, txHash: string, status: string) =
self.clearTmpData(self.tmpKeepPinPass)
self.view.sendtransactionSendingCompleteSignal(txHash, status) self.view.sendtransactionSendingCompleteSignal(txHash, status)

View File

@ -180,7 +180,7 @@ QtObject:
slippagePercentage = slippagePercentageString.parseFloat() slippagePercentage = slippagePercentageString.parseFloat()
except: except:
error "parsing slippage failed", slippage=slippagePercentageString error "parsing slippage failed", slippage=slippagePercentageString
self.delegate.authenticateAndTransferV2(self.selectedSenderAccountAddress, uuid, slippagePercentage) self.delegate.authenticateAndTransfer(self.selectedSenderAccountAddress, uuid, slippagePercentage)
proc suggestedRoutesReady*(self: View, suggestedRoutes: QVariant, errCode: string, errDescription: string) {.signal.} proc suggestedRoutesReady*(self: View, suggestedRoutes: QVariant, errCode: string, errDescription: string) {.signal.}
proc setTransactionRoute*(self: View, routes: TransactionRoutes, errCode: string, errDescription: string) = proc setTransactionRoute*(self: View, routes: TransactionRoutes, errCode: string, errDescription: string) =

View File

@ -129,7 +129,6 @@ QtObject:
break break
if ensDto.username.len == 0: if ensDto.username.len == 0:
error "Error updating ens username status", message = "no ens username found for transactionHash: " & transactionHash
return return
let key = makeKey(ensDto.username, chainId) let key = makeKey(ensDto.username, chainId)

View File

@ -363,6 +363,8 @@ Item {
verify(!!headerContentItemEmoji) verify(!!headerContentItemEmoji)
compare(headerContentItemEmoji.asset.emoji, swapAdaptor.nonWatchAccounts.get(i).emoji) compare(headerContentItemEmoji.asset.emoji, swapAdaptor.nonWatchAccounts.get(i).emoji)
waitForRendering(amountToSendInput)
verify(amountToSendInput.cursorVisible) verify(amountToSendInput.cursorVisible)
} }
closeAndVerfyModal() closeAndVerfyModal()
@ -1399,6 +1401,7 @@ Item {
launchAndVerfyModal() launchAndVerfyModal()
waitForRendering(payPanel) waitForRendering(payPanel)
waitForRendering(receivePanel) waitForRendering(receivePanel)
waitForRendering(payAmountToSendInput)
let paytokenSelectorContentItemText = findChild(payPanel, "tokenSelectorContentItemText") let paytokenSelectorContentItemText = findChild(payPanel, "tokenSelectorContentItemText")
verify(!!paytokenSelectorContentItemText) verify(!!paytokenSelectorContentItemText)

View File

@ -99,8 +99,7 @@ Item {
controlUnderTest.toTokenAmount = "1.42" controlUnderTest.toTokenAmount = "1.42"
controlUnderTest.toTokenContractAddress = "0xdeadcaff" controlUnderTest.toTokenContractAddress = "0xdeadcaff"
// title & subtitle // subtitle
compare(controlUnderTest.title, qsTr("Sign Swap"))
compare(controlUnderTest.subtitle, qsTr("%1 to %2").arg(controlUnderTest.formatBigNumber(controlUnderTest.fromTokenAmount, controlUnderTest.fromTokenSymbol)) compare(controlUnderTest.subtitle, qsTr("%1 to %2").arg(controlUnderTest.formatBigNumber(controlUnderTest.fromTokenAmount, controlUnderTest.fromTokenSymbol))
.arg(controlUnderTest.formatBigNumber(controlUnderTest.toTokenAmount, controlUnderTest.toTokenSymbol))) .arg(controlUnderTest.formatBigNumber(controlUnderTest.toTokenAmount, controlUnderTest.toTokenSymbol)))

View File

@ -36,8 +36,12 @@ StatusDialog {
property Component headerIconComponent property Component headerIconComponent
property bool feesLoading property bool feesLoading
property string signButtonText: qsTr("Sign")
property bool signButtonEnabled: true property bool signButtonEnabled: true
property string closeButtonText: qsTr("Close")
property date requestTimestamp: new Date() property date requestTimestamp: new Date()
property int expirationSeconds property int expirationSeconds
property bool hasExpiryDate: false property bool hasExpiryDate: false
@ -61,14 +65,14 @@ StatusDialog {
visible: !root.hasExpiryDate || !countdownPill.isExpired visible: !root.hasExpiryDate || !countdownPill.isExpired
icon.name: Constants.authenticationIconByType[root.loginType] icon.name: Constants.authenticationIconByType[root.loginType]
disabledColor: Theme.palette.directColor8 disabledColor: Theme.palette.directColor8
text: qsTr("Sign") text: root.signButtonText
onClicked: root.accept() // close and emit accepted() signal onClicked: root.accept() // close and emit accepted() signal
} }
StatusButton { StatusButton {
objectName: "closeButton" objectName: "closeButton"
id: closeButton id: closeButton
visible: root.hasExpiryDate && countdownPill.isExpired visible: root.hasExpiryDate && countdownPill.isExpired
text: qsTr("Close") text: root.closeButtonText
onClicked: root.close() onClicked: root.close()
} }
} }

View File

@ -397,12 +397,14 @@ StatusDialog {
: Constants.authenticationIconByType[root.loginType] : Constants.authenticationIconByType[root.loginType]
text: { text: {
if(root.swapAdaptor.validSwapProposalReceived) { if(root.swapAdaptor.validSwapProposalReceived) {
if(root.swapAdaptor.swapOutputData.approvalNeeded) {
if (root.swapAdaptor.approvalPending) { if (root.swapAdaptor.approvalPending) {
return qsTr("Approving %1").arg(fromTokenSymbol) return qsTr("Approving %1").arg(fromTokenSymbol)
} else if(root.swapAdaptor.swapOutputData.approvalNeeded) { } else if(!root.swapAdaptor.approvalSuccessful) {
return qsTr("Approve %1").arg(fromTokenSymbol) return qsTr("Approve %1").arg(fromTokenSymbol)
} }
} }
}
return qsTr("Swap") return qsTr("Swap")
} }
tooltip.text: root.swapAdaptor.validSwapProposalReceived && tooltip.text: root.swapAdaptor.validSwapProposalReceived &&
@ -416,7 +418,7 @@ StatusDialog {
onClicked: { onClicked: {
if (root.swapAdaptor.validSwapProposalReceived) { if (root.swapAdaptor.validSwapProposalReceived) {
d.addMetricsEvent("next button pressed") d.addMetricsEvent("next button pressed")
if (root.swapAdaptor.swapOutputData.approvalNeeded) if (root.swapAdaptor.swapOutputData.approvalNeeded && !root.swapAdaptor.approvalSuccessful)
Global.openPopup(swapApproveModalComponent) Global.openPopup(swapApproveModalComponent)
else else
Global.openPopup(swapSignModalComponent) Global.openPopup(swapSignModalComponent)
@ -483,6 +485,9 @@ StatusDialog {
SwapSignModal { SwapSignModal {
destroyOnClose: true destroyOnClose: true
title: root.swapAdaptor.swapOutputData.approvalNeeded && root.swapAdaptor.approvalSuccessful? qsTr("Swap") : qsTr("Sign Swap")
signButtonText: root.swapAdaptor.swapOutputData.approvalNeeded && root.swapAdaptor.approvalSuccessful? qsTr("Swap") : qsTr("Sign")
formatBigNumber: (number, symbol, noSymbolOption) => root.swapAdaptor.currencyStore.formatBigNumber(number, symbol, noSymbolOption) formatBigNumber: (number, symbol, noSymbolOption) => root.swapAdaptor.currencyStore.formatBigNumber(number, symbol, noSymbolOption)
loginType: root.swapAdaptor.selectedAccount.migratedToKeycard ? Constants.LoginType.Keycard : root.loginType loginType: root.swapAdaptor.selectedAccount.migratedToKeycard ? Constants.LoginType.Keycard : root.loginType

View File

@ -87,8 +87,6 @@ QObject {
readonly property bool isEthBalanceInsufficient: d.isEthBalanceInsufficient readonly property bool isEthBalanceInsufficient: d.isEthBalanceInsufficient
readonly property bool isTokenBalanceInsufficient: d.isTokenBalanceInsufficient readonly property bool isTokenBalanceInsufficient: d.isTokenBalanceInsufficient
signal suggestedRoutesReady()
QtObject { QtObject {
id: d id: d
@ -252,11 +250,10 @@ QObject {
root.swapOutputData.hasError = true root.swapOutputData.hasError = true
} }
root.swapOutputData.hasError = root.swapOutputData.hasError || root.swapOutputData.errCode !== "" root.swapOutputData.hasError = root.swapOutputData.hasError || root.swapOutputData.errCode !== ""
root.suggestedRoutesReady()
} }
function onTransactionSent(uuid, chainId, approvalTx, txHash, error) { function onTransactionSent(uuid, chainId, approvalTx, txHash, error) {
if(root.swapOutputData.approvalNeeded) { if(root.swapOutputData.approvalNeeded && !root.approvalSuccessful) {
if (uuid !== d.uuid || !!error) { if (uuid !== d.uuid || !!error) {
root.approvalPending = false root.approvalPending = false
root.approvalSuccessful = false root.approvalSuccessful = false
@ -272,10 +269,6 @@ QObject {
root.approvalPending = false root.approvalPending = false
root.approvalSuccessful = status == "Success" // TODO: make a all tx statuses Constants (success, pending, failed) root.approvalSuccessful = status == "Success" // TODO: make a all tx statuses Constants (success, pending, failed)
d.txHash = "" d.txHash = ""
if (root.approvalSuccessful) {
root.swapOutputData.approvalNeeded = false
}
} }
} }
} }
@ -335,14 +328,10 @@ QObject {
function sendApproveTx() { function sendApproveTx() {
root.approvalPending = true root.approvalPending = true
const accountAddress = root.swapFormData.selectedAccountAddress
root.swapStore.authenticateAndTransfer(d.uuid, "") root.swapStore.authenticateAndTransfer(d.uuid, "")
} }
function sendSwapTx() { function sendSwapTx() {
const accountAddress = root.swapFormData.selectedAccountAddress
root.swapStore.authenticateAndTransfer(d.uuid, root.swapFormData.selectedSlippage) root.swapStore.authenticateAndTransfer(d.uuid, root.swapFormData.selectedSlippage)
} }
} }

View File

@ -45,7 +45,6 @@ SignTransactionModalBase {
required property string serviceProviderURL required property string serviceProviderURL
required property string serviceProviderTandCUrl required property string serviceProviderTandCUrl
title: qsTr("Sign Swap")
//: e.g. (swap) 100 DAI to 100 USDT //: e.g. (swap) 100 DAI to 100 USDT
subtitle: qsTr("%1 to %2").arg(formatBigNumber(fromTokenAmount, fromTokenSymbol)).arg(formatBigNumber(toTokenAmount, toTokenSymbol)) subtitle: qsTr("%1 to %2").arg(formatBigNumber(fromTokenAmount, fromTokenSymbol)).arg(formatBigNumber(toTokenAmount, toTokenSymbol))