feat(dapps) check Wallet Connect pair uri for already used and expired
Refactor and bring all the error check back to services Automate pairing procedure and move to the connect dialog if uri was validated Updates: #14676
This commit is contained in:
parent
2df35ff0c9
commit
15b2c084d2
|
@ -201,12 +201,9 @@ Item {
|
|||
if (d.activeTestCase < d.openPairTestCase)
|
||||
return
|
||||
|
||||
let items = InspectionUtils.findVisualsByTypeName(dappsWorkflow, "DAppsListPopup")
|
||||
if (items.length === 1) {
|
||||
let buttons = InspectionUtils.findVisualsByTypeName(items[0], "StatusButton")
|
||||
if (buttons.length === 1) {
|
||||
buttons[0].clicked()
|
||||
}
|
||||
let buttons = InspectionUtils.findVisualsByTypeName(dappsWorkflow.popup, "StatusButton")
|
||||
if (buttons.length === 1) {
|
||||
buttons[0].clicked()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -232,7 +229,7 @@ Item {
|
|||
let modals = InspectionUtils.findVisualsByTypeName(dappsWorkflow, "PairWCModal")
|
||||
if (modals.length === 1) {
|
||||
let buttons = InspectionUtils.findVisualsByTypeName(modals[0].footer, "StatusButton")
|
||||
if (buttons.length === 1 && walletConnectService.wcSDK.sdkReady) {
|
||||
if (buttons.length === 1 && buttons[0].enabled && walletConnectService.wcSDK.sdkReady) {
|
||||
d.activeTestCase = d.noTestCase
|
||||
buttons[0].clicked()
|
||||
return
|
||||
|
@ -473,7 +470,7 @@ Item {
|
|||
property bool testNetworks: false
|
||||
property bool enableSDK: true
|
||||
property bool pending : false
|
||||
property string customAccounts: ""
|
||||
property string customAccounts: "[]"
|
||||
property string persistedSessions: "[]"
|
||||
}
|
||||
|
||||
|
|
|
@ -47,8 +47,12 @@ DappsComboBox {
|
|||
onClosed: pairWCLoader.active = false
|
||||
|
||||
onPair: (uri) => {
|
||||
root.wcService.pair(uri)
|
||||
this.isPairing = true
|
||||
root.wcService.pair(uri)
|
||||
}
|
||||
|
||||
onPairUriChanged: (uri) => {
|
||||
root.wcService.validatePairingUri(uri)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -151,8 +155,10 @@ DappsComboBox {
|
|||
function onMaxFeesUpdated(maxFees, maxFeesWei, haveEnoughFunds, symbol) {
|
||||
maxFeesText = `${maxFees.toFixed(2)} ${symbol}`
|
||||
var ethStr = "?"
|
||||
if (globalUtils) {
|
||||
try {
|
||||
ethStr = globalUtils.wei2Eth(maxFeesWei, 9)
|
||||
} catch (e) {
|
||||
// ignore error in case of tests and storybook where we don't have access to globalUtils
|
||||
}
|
||||
maxFeesEthText = `${ethStr} ETH`
|
||||
enoughFunds = haveEnoughFunds
|
||||
|
@ -179,6 +185,12 @@ DappsComboBox {
|
|||
Connections {
|
||||
target: root.wcService
|
||||
|
||||
function onPairingUriValidated(validationState) {
|
||||
if (pairWCLoader.item) {
|
||||
pairWCLoader.item.pairingUriValidated(validationState)
|
||||
}
|
||||
}
|
||||
|
||||
function onConnectDApp(dappChains, sessionProposal, availableNamespaces) {
|
||||
connectDappLoader.dappChains = dappChains
|
||||
connectDappLoader.sessionProposal = sessionProposal
|
||||
|
|
|
@ -45,6 +45,38 @@ QObject {
|
|||
}
|
||||
readonly property var flatNetworks: root.walletRootStore.filteredFlatModel
|
||||
|
||||
function validatePairingUri(uri) {
|
||||
if(Helpers.containsOnlyEmoji(uri)) {
|
||||
root.pairingUriValidated(Pairing.uriErrors.tooCool)
|
||||
return
|
||||
} else if(!Helpers.validURI(uri)) {
|
||||
root.pairingUriValidated(Pairing.uriErrors.invalidUri)
|
||||
return
|
||||
}
|
||||
|
||||
let info = Helpers.extractInfoFromPairUri(uri)
|
||||
wcSDK.getActiveSessions((sessions) => {
|
||||
// Check if the URI is already paired
|
||||
var validationState = Pairing.uriErrors.ok
|
||||
for (let key in sessions) {
|
||||
if (sessions[key].pairingTopic == info.topic) {
|
||||
validationState = Pairing.uriErrors.alreadyUsed
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Check if expired
|
||||
if (validationState == Pairing.uriErrors.ok) {
|
||||
const now = (new Date().getTime())/1000
|
||||
if (info.expiry < now) {
|
||||
validationState = Pairing.uriErrors.expired
|
||||
}
|
||||
}
|
||||
|
||||
root.pairingUriValidated(validationState)
|
||||
});
|
||||
}
|
||||
|
||||
function pair(uri) {
|
||||
d.acceptedSessionProposal = null
|
||||
wcSDK.pair(uri)
|
||||
|
@ -84,6 +116,7 @@ QObject {
|
|||
signal approveSessionResult(var session, var error)
|
||||
signal sessionRequest(SessionRequestResolved request)
|
||||
signal displayToastMessage(string message, bool error)
|
||||
signal pairingUriValidated(int validationState)
|
||||
|
||||
readonly property Connections sdkConnections: Connections {
|
||||
target: wcSDK
|
||||
|
|
|
@ -66,4 +66,37 @@ function buildSupportedNamespaces(chainIds, addresses, methods) {
|
|||
let methodsStr = methods.map(method => `"${method}"`).join(',')
|
||||
return `{
|
||||
"eip155":{"chains": [${eipChainIds.join(',')}],"methods": [${methodsStr}],"events": ["accountsChanged", "chainChanged"],"accounts": [${eipAddresses.join(',')}]}}`
|
||||
}
|
||||
|
||||
function validURI(uri) {
|
||||
var regex = /^wc:[0-9a-fA-F-]*@([1-9][0-9]*)(\?([a-zA-Z-]+=[^&]+)(&[a-zA-Z-]+=[^&]+)*)?$/
|
||||
return regex.test(uri)
|
||||
}
|
||||
|
||||
function containsOnlyEmoji(uri) {
|
||||
var emojiRegex = new RegExp("[\\u203C-\\u3299\\u1F000-\\u1F644]");
|
||||
return !emojiRegex.test(uri);
|
||||
}
|
||||
|
||||
function extractInfoFromPairUri(uri) {
|
||||
var topic = ""
|
||||
var expiry = NaN
|
||||
// Extract topic and expiry from wc:99fdcac5cc081ac8c1181b4c38c5dc49fb5eb212706d5c94c445be549765e7f0@2?expiryTimestamp=1720090818&relay-protocol=irn&symKey=c6b67d94174bd42d16ff288220ce9b8966e5b56a2d3570a30d5b0a760f1953f0
|
||||
const regex = /wc:([0-9a-fA-F]*)/
|
||||
const match = uri.match(regex)
|
||||
if (match) {
|
||||
topic = match[1]
|
||||
}
|
||||
|
||||
var parts = uri.split('?')
|
||||
if (parts.length > 1) {
|
||||
var params = parts[1].split('&')
|
||||
for (let i = 0; i < params.length; i++) {
|
||||
var keyVal = params[i].split('=')
|
||||
if (keyVal[0] === 'expiryTimestamp') {
|
||||
expiry = parseInt(keyVal[1])
|
||||
}
|
||||
}
|
||||
}
|
||||
return { topic, expiry }
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
pragma Singleton
|
||||
|
||||
import QtQml 2.15
|
||||
|
||||
QtObject {
|
||||
readonly property QtObject uriErrors: QtObject {
|
||||
readonly property int notChecked: 0
|
||||
readonly property int ok: 1
|
||||
readonly property int tooCool: 2
|
||||
readonly property int invalidUri: 3
|
||||
readonly property int alreadyUsed: 4
|
||||
readonly property int expired: 5
|
||||
}
|
||||
}
|
|
@ -8,7 +8,7 @@ QtObject {
|
|||
/// Supported methods
|
||||
/// userString is used in the context `dapp.url #{userString} <accepted/rejected>`
|
||||
/// requestDisplay is used in the context `dApp wants you to ${requestDisplay} with <Account Name Here>`
|
||||
property QtObject methods: QtObject {
|
||||
readonly property QtObject methods: QtObject {
|
||||
readonly property QtObject personalSign: QtObject {
|
||||
readonly property string name: Constants.personal_sign
|
||||
readonly property string userString: qsTr("sign")
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
SessionRequestResolved 1.0 SessionRequestResolved.qml
|
||||
singleton SessionRequest 1.0 SessionRequest.qml
|
||||
singleton SessionRequest 1.0 SessionRequest.qml
|
||||
singleton Pairing 1.0 Pairing.qml
|
|
@ -13,6 +13,8 @@ import utils 1.0
|
|||
import shared.controls 1.0
|
||||
import shared.popups 1.0
|
||||
|
||||
import AppLayouts.Wallet.services.dapps.types 1.0
|
||||
|
||||
import "PairWCModal"
|
||||
|
||||
StatusDialog {
|
||||
|
@ -25,7 +27,15 @@ StatusDialog {
|
|||
|
||||
property bool isPairing: false
|
||||
|
||||
function pairingUriValidated(validationState) {
|
||||
uriInput.errorState = validationState
|
||||
if (validationState === Pairing.uriErrors.ok) {
|
||||
d.doPair()
|
||||
}
|
||||
}
|
||||
|
||||
signal pair(string uri)
|
||||
signal pairUriChanged(string uri)
|
||||
|
||||
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
|
||||
|
||||
|
@ -41,7 +51,12 @@ StatusDialog {
|
|||
WCUriInput {
|
||||
id: uriInput
|
||||
|
||||
onTextChanged: root.isPairing = false
|
||||
pending: uriInput.errorState === Pairing.uriErrors.notChecked
|
||||
|
||||
onTextChanged: {
|
||||
root.isPairing = false
|
||||
root.pairUriChanged(uriInput.text)
|
||||
}
|
||||
}
|
||||
|
||||
// Spacer
|
||||
|
@ -73,10 +88,24 @@ StatusDialog {
|
|||
height: 44
|
||||
text: qsTr("Done")
|
||||
|
||||
enabled: uriInput.valid && !root.isPairing && uriInput.text.length > 0
|
||||
enabled: uriInput.valid
|
||||
&& !root.isPairing
|
||||
&& uriInput.text.length > 0
|
||||
&& uriInput.errorState === Pairing.uriErrors.ok
|
||||
|
||||
onClicked: root.pair(uriInput.text)
|
||||
onClicked: {
|
||||
d.doPair()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: d
|
||||
|
||||
function doPair() {
|
||||
root.isPairing = true
|
||||
root.pair(uriInput.text)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,12 +9,15 @@ import StatusQ.Controls 0.1
|
|||
import StatusQ.Components 0.1
|
||||
import StatusQ.Core.Theme 0.1
|
||||
|
||||
import AppLayouts.Wallet.services.dapps.types 1.0
|
||||
|
||||
ColumnLayout {
|
||||
id: root
|
||||
|
||||
readonly property bool valid: input.valid && input.text.length > 0
|
||||
readonly property alias text: input.text
|
||||
property alias pending: input.pending
|
||||
property int errorState: Pairing.uriErrors.notChecked
|
||||
|
||||
StatusBaseInput {
|
||||
id: input
|
||||
|
@ -29,49 +32,27 @@ ColumnLayout {
|
|||
valid: {
|
||||
let uri = input.text
|
||||
|
||||
errorText.text = ""
|
||||
if(uri.length === 0) {
|
||||
errorText.text = ""
|
||||
return true
|
||||
}
|
||||
|
||||
if(containsOnlyEmoji(uri)) {
|
||||
if(root.errorState === Pairing.uriErrors.tooCool) {
|
||||
errorText.text = qsTr("WalletConnect URI too cool")
|
||||
return false
|
||||
} else if(!validURI(uri)) {
|
||||
} else if(root.errorState === Pairing.uriErrors.invalidUri) {
|
||||
errorText.text = qsTr("WalletConnect URI invalid")
|
||||
return false
|
||||
} else if(wcUriAlreadyUsed(uri)) {
|
||||
} else if(root.errorState === Pairing.uriErrors.alreadyUsed) {
|
||||
errorText.text = qsTr("WalletConnect URI already used")
|
||||
return false
|
||||
} else if(wcUriExpired(uri)) {
|
||||
} else if(root.errorState === Pairing.uriErrors.expired) {
|
||||
errorText.text = qsTr("WalletConnect URI has expired")
|
||||
}
|
||||
if (errorText.text.length > 0) {
|
||||
return false
|
||||
}
|
||||
|
||||
errorText.text = ""
|
||||
return true
|
||||
}
|
||||
|
||||
function validURI(uri) {
|
||||
var regex = /^wc:[0-9a-fA-F-]*@([1-9][0-9]*)(\?([a-zA-Z-]+=[^&]+)(&[a-zA-Z-]+=[^&]+)*)?$/
|
||||
return regex.test(uri)
|
||||
}
|
||||
|
||||
function containsOnlyEmoji(uri) {
|
||||
var emojiRegex = new RegExp("[\\u203C-\\u3299\\u1F000-\\u1F644]");
|
||||
return !emojiRegex.test(uri);
|
||||
}
|
||||
|
||||
function wcUriAlreadyUsed(uri) {
|
||||
// TODO: Check if URI is already used
|
||||
return false
|
||||
}
|
||||
|
||||
function wcUriExpired(uri) {
|
||||
// TODO: Check if URI is expired
|
||||
return false
|
||||
}
|
||||
|
||||
rightComponent: Item {
|
||||
width: pasteButton.implicitWidth
|
||||
height: pasteButton.implicitHeight
|
||||
|
|
Loading…
Reference in New Issue