Skip to content

Commit

Permalink
chore: sign the flow only once if approval and transaction need to be…
Browse files Browse the repository at this point in the history
… placed once after another (swap flow)

Closes #16337
  • Loading branch information
saledjenic committed Oct 9, 2024
1 parent 11f1e15 commit 6bec467
Show file tree
Hide file tree
Showing 10 changed files with 60 additions and 56 deletions.
2 changes: 1 addition & 1 deletion src/app/modules/main/wallet_section/send/io_interface.nim
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ method stopUpdatesForSuggestedRoute*(self: AccessInterface) {.base.} =
method suggestedRoutesReady*(self: AccessInterface, uuid: string, suggestedRoutes: SuggestedRoutesDto, errCode: string, errDescription: string) {.base.} =
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")

method onUserAuthenticated*(self: AccessInterface, password: string, pin: string) {.base.} =
Expand Down
68 changes: 37 additions & 31 deletions src/app/modules/main/wallet_section/send/module.nim
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,13 @@ const authenticationCanceled* = "authenticationCanceled"

# Shouldn't be public ever, use only within this module.
type TmpSendTransactionDetails = object
fromAddr: string
fromAddrPath: string
toAddr: string
assetKey: string
toAssetKey: string
paths: seq[TransactionPathDto]
uuid: string
sendType: SendType
pin: string
password: string
txHashBeingProcessed: string
resolvedSignatures: TransactionsSignatures
tokenName: string
isOwnerToken: bool
slippagePercentage: float

type
Expand All @@ -48,9 +44,7 @@ type
controller: controller.Controller
moduleLoaded: bool
tmpSendTransactionDetails: TmpSendTransactionDetails
tmpPin: string
tmpPassword: string
tmpTxHashBeingProcessed: string
tmpKeepPinPass: bool

# Forward declaration
method getTokenBalance*(self: Module, address: string, chainId: int, tokensKey: string): CurrencyAmount
Expand Down Expand Up @@ -79,12 +73,15 @@ method delete*(self: Module) =
self.view.delete
self.controller.delete

proc clearTmpData(self: Module) =
self.tmpPin = ""
self.tmpPassword = ""
self.tmpTxHashBeingProcessed = ""
proc clearTmpData(self: Module, keepPinPass = false) =
if keepPinPass:
self.tmpSendTransactionDetails = TmpSendTransactionDetails(
sendType: self.tmpSendTransactionDetails.sendType,
pin: self.tmpSendTransactionDetails.pin,
password: self.tmpSendTransactionDetails.password
)
return
self.tmpSendTransactionDetails = TmpSendTransactionDetails()
writeStackTrace()

proc convertSendToNetworkToNetworkItem(self: Module, network: SendToNetwork): NetworkRouteItem =
result = initNetworkRouteItem(
Expand Down Expand Up @@ -183,11 +180,22 @@ method getNetworkItem*(self: Module, chainId: int): network_service_item.Network
return nil
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.slippagePercentage = slippagePercentage
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)
if kp.migratedToKeycard():
let accounts = kp.accounts.filter(acc => cmpIgnoreCase(acc.address, fromAddr) == 0)
Expand All @@ -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.clearTmpData()
else:
self.tmpPin = pin
self.tmpPassword = password
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()
self.tmpSendTransactionDetails.pin = pin
self.tmpSendTransactionDetails.password = password
self.buildTransactionsFromRoute()

proc sendSignedTransactions*(self: Module) =
try:
Expand All @@ -226,17 +231,17 @@ proc sendSignedTransactions*(self: Module) =
self.clearTmpData()

proc signOnKeycard(self: Module) =
self.tmpTxHashBeingProcessed = ""
self.tmpSendTransactionDetails.txHashBeingProcessed = ""
for h, (r, s, v) in self.tmpSendTransactionDetails.resolvedSignatures.pairs:
if r.len != 0 and s.len != 0 and v.len != 0:
continue
self.tmpTxHashBeingProcessed = h
var txForKcFlow = self.tmpTxHashBeingProcessed
self.tmpSendTransactionDetails.txHashBeingProcessed = h
var txForKcFlow = self.tmpSendTransactionDetails.txHashBeingProcessed
if txForKcFlow.startsWith("0x"):
txForKcFlow = txForKcFlow[2..^1]
self.controller.runSignFlow(self.tmpPin, self.tmpSendTransactionDetails.fromAddrPath, txForKcFlow)
self.controller.runSignFlow(self.tmpSendTransactionDetails.pin, self.tmpSendTransactionDetails.fromAddrPath, txForKcFlow)
break
if self.tmpTxHashBeingProcessed.len == 0:
if self.tmpSendTransactionDetails.txHashBeingProcessed.len == 0:
self.sendSignedTransactions()
self.clearTmpData()

Expand Down Expand Up @@ -265,7 +270,7 @@ method prepareSignaturesForTransactions*(self:Module, txForSigning: RouterTransa
self.tmpSendTransactionDetails.resolvedSignatures[h] = ("", "", "")
self.signOnKeycard()
else:
let finalPassword = hashPassword(self.tmpPassword)
let finalPassword = hashPassword(self.tmpSendTransactionDetails.password)
for h in txForSigning.signingDetails.hashes:
self.tmpSendTransactionDetails.resolvedSignatures[h] = ("", "", "")
var
Expand All @@ -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.clearTmpData()
return
self.tmpSendTransactionDetails.resolvedSignatures[self.tmpTxHashBeingProcessed] = (keycardEvent.txSignature.r,
self.tmpSendTransactionDetails.resolvedSignatures[self.tmpSendTransactionDetails.txHashBeingProcessed] = (keycardEvent.txSignature.r,
keycardEvent.txSignature.s, keycardEvent.txSignature.v)
self.signOnKeycard()

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:
self.view.sendTransactionSentSignal(uuid = self.tmpSendTransactionDetails.uuid, chainId = 0, approvalTx = false, txHash = "", error)
return
self.view.sendTransactionSentSignal(uuid, chainId, approvalTx, txHash, error)

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 suggestedRouteModel = newSuggestedRouteModel()
suggestedRouteModel.setItems(paths)
Expand Down Expand Up @@ -332,6 +336,7 @@ method suggestedRoutes*(self: Module,
disabledToChainIDs: seq[int] = @[],
lockedInAmounts: Table[string, string] = initTable[string, string](),
extraParamsTable: Table[string, string] = initTable[string, string]()) =
self.tmpSendTransactionDetails.sendType = sendType
self.controller.suggestedRoutes(
uuid,
sendType,
Expand Down Expand Up @@ -403,4 +408,5 @@ method splitAndFormatAddressPrefix*(self: Module, text : string, updateInStore:
return editedText

method transactionSendingComplete*(self: Module, txHash: string, status: string) =
self.clearTmpData(self.tmpKeepPinPass)
self.view.sendtransactionSendingCompleteSignal(txHash, status)
2 changes: 1 addition & 1 deletion src/app/modules/main/wallet_section/send/view.nim
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ QtObject:
slippagePercentage = slippagePercentageString.parseFloat()
except:
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 setTransactionRoute*(self: View, routes: TransactionRoutes, errCode: string, errDescription: string) =
Expand Down
1 change: 0 additions & 1 deletion src/app_service/service/ens/service.nim
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,6 @@ QtObject:
break

if ensDto.username.len == 0:
error "Error updating ens username status", message = "no ens username found for transactionHash: " & transactionHash
return

let key = makeKey(ensDto.username, chainId)
Expand Down
3 changes: 3 additions & 0 deletions storybook/qmlTests/tests/tst_SwapModal.qml
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,8 @@ Item {
verify(!!headerContentItemEmoji)
compare(headerContentItemEmoji.asset.emoji, swapAdaptor.nonWatchAccounts.get(i).emoji)

waitForRendering(amountToSendInput)

verify(amountToSendInput.cursorVisible)
}
closeAndVerfyModal()
Expand Down Expand Up @@ -1398,6 +1400,7 @@ Item {
launchAndVerfyModal()
waitForRendering(payPanel)
waitForRendering(receivePanel)
waitForRendering(payAmountToSendInput)

let paytokenSelectorContentItemText = findChild(payPanel, "tokenSelectorContentItemText")
verify(!!paytokenSelectorContentItemText)
Expand Down
3 changes: 1 addition & 2 deletions storybook/qmlTests/tests/tst_SwapSignModal.qml
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,7 @@ Item {
controlUnderTest.toTokenAmount = "1.42"
controlUnderTest.toTokenContractAddress = "0xdeadcaff"

// title & subtitle
compare(controlUnderTest.title, qsTr("Sign Swap"))
// subtitle
compare(controlUnderTest.subtitle, qsTr("%1 to %2").arg(controlUnderTest.formatBigNumber(controlUnderTest.fromTokenAmount, controlUnderTest.fromTokenSymbol))
.arg(controlUnderTest.formatBigNumber(controlUnderTest.toTokenAmount, controlUnderTest.toTokenSymbol)))

Expand Down
8 changes: 6 additions & 2 deletions ui/app/AppLayouts/Wallet/popups/SignTransactionModalBase.qml
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,12 @@ StatusDialog {
property Component headerIconComponent

property bool feesLoading

property string signButtonText: qsTr("Sign")
property bool signButtonEnabled: true

property string closeButtonText: qsTr("Close")

property date requestTimestamp: new Date()
property int expirationSeconds

Expand All @@ -60,14 +64,14 @@ StatusDialog {
visible: !countdownPill.isExpired
icon.name: Constants.authenticationIconByType[root.loginType]
disabledColor: Theme.palette.directColor8
text: qsTr("Sign")
text: root.signButtonText
onClicked: root.accept() // close and emit accepted() signal
}
StatusButton {
objectName: "closeButton"
id: closeButton
visible: countdownPill.isExpired
text: qsTr("Close")
text: root.closeButtonText
onClicked: root.close()
}
}
Expand Down
15 changes: 10 additions & 5 deletions ui/app/AppLayouts/Wallet/popups/swap/SwapModal.qml
Original file line number Diff line number Diff line change
Expand Up @@ -397,10 +397,12 @@ StatusDialog {
: Constants.authenticationIconByType[root.loginType]
text: {
if(root.swapAdaptor.validSwapProposalReceived) {
if (root.swapAdaptor.approvalPending) {
return qsTr("Approving %1").arg(fromTokenSymbol)
} else if(root.swapAdaptor.swapOutputData.approvalNeeded) {
return qsTr("Approve %1").arg(fromTokenSymbol)
if(root.swapAdaptor.swapOutputData.approvalNeeded) {
if (root.swapAdaptor.approvalPending) {
return qsTr("Approving %1").arg(fromTokenSymbol)
} else if(!root.swapAdaptor.approvalSuccessful) {
return qsTr("Approve %1").arg(fromTokenSymbol)
}
}
}
return qsTr("Swap")
Expand All @@ -416,7 +418,7 @@ StatusDialog {
onClicked: {
if (root.swapAdaptor.validSwapProposalReceived) {
d.addMetricsEvent("next button pressed")
if (root.swapAdaptor.swapOutputData.approvalNeeded)
if (root.swapAdaptor.swapOutputData.approvalNeeded && !root.swapAdaptor.approvalSuccessful)
Global.openPopup(swapApproveModalComponent)
else
Global.openPopup(swapSignModalComponent)
Expand Down Expand Up @@ -483,6 +485,9 @@ StatusDialog {
SwapSignModal {
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)

loginType: root.swapAdaptor.selectedAccount.migratedToKeycard ? Constants.LoginType.Keycard : root.loginType
Expand Down
13 changes: 1 addition & 12 deletions ui/app/AppLayouts/Wallet/popups/swap/SwapModalAdaptor.qml
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,6 @@ QObject {
readonly property bool isEthBalanceInsufficient: d.isEthBalanceInsufficient
readonly property bool isTokenBalanceInsufficient: d.isTokenBalanceInsufficient

signal suggestedRoutesReady()

QtObject {
id: d

Expand Down Expand Up @@ -261,11 +259,10 @@ QObject {
root.swapOutputData.hasError = true
}
root.swapOutputData.hasError = root.swapOutputData.hasError || root.swapOutputData.errCode !== ""
root.suggestedRoutesReady()
}

function onTransactionSent(uuid, chainId, approvalTx, txHash, error) {
if(root.swapOutputData.approvalNeeded) {
if(root.swapOutputData.approvalNeeded && !root.approvalSuccessful) {
if (uuid !== d.uuid || !!error) {
root.approvalPending = false
root.approvalSuccessful = false
Expand All @@ -281,10 +278,6 @@ QObject {
root.approvalPending = false
root.approvalSuccessful = status == "Success" // TODO: make a all tx statuses Constants (success, pending, failed)
d.txHash = ""

if (root.approvalSuccessful) {
root.swapOutputData.approvalNeeded = false
}
}
}
}
Expand Down Expand Up @@ -344,14 +337,10 @@ QObject {

function sendApproveTx() {
root.approvalPending = true
const accountAddress = root.swapFormData.selectedAccountAddress

root.swapStore.authenticateAndTransfer(d.uuid, "")
}

function sendSwapTx() {
const accountAddress = root.swapFormData.selectedAccountAddress

root.swapStore.authenticateAndTransfer(d.uuid, root.swapFormData.selectedSlippage)
}
}
1 change: 0 additions & 1 deletion ui/app/AppLayouts/Wallet/popups/swap/SwapSignModal.qml
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ SignTransactionModalBase {
required property string serviceProviderURL
required property string serviceProviderTandCUrl

title: qsTr("Sign Swap")
//: e.g. (swap) 100 DAI to 100 USDT
subtitle: qsTr("%1 to %2").arg(formatBigNumber(fromTokenAmount, fromTokenSymbol)).arg(formatBigNumber(toTokenAmount, toTokenSymbol))

Expand Down

0 comments on commit 6bec467

Please sign in to comment.