import QtQuick 2.15 import QtTest 1.15 import AppLayouts.Wallet.services.dapps 1.0 import AppLayouts.Wallet.services.dapps.plugins 1.0 import AppLayouts.Wallet.services.dapps.types 1.0 import shared.stores 1.0 import utils 1.0 Item { id: root Component { id: signRequestPluginComponent SignRequestPlugin { id: plugin property SignalSpy acceptedSpy: SignalSpy { target: plugin; signalName: "accepted" } property SignalSpy rejectedSpy: SignalSpy { target: plugin; signalName: "rejected" } property SignalSpy signCompletedSpy: SignalSpy { target: plugin; signalName: "signCompleted" } sdk: WalletConnectSDKBase { id: sdk enabled: true projectId: "" property bool sdkReady: true property var getActiveSessionsCallbacks: [] getActiveSessions: function(callback) { getActiveSessionsCallbacks.push({callback}) } property var acceptSessionRequestCalls: [] acceptSessionRequest: function(topic, id, signature) { acceptSessionRequestCalls.push({topic, id, signature}) } property var rejectSessionRequestCalls: [] rejectSessionRequest: function(topic, id, error) { rejectSessionRequestCalls.push({topic, id, error}) } } store: DAppsStore { id: dappsStore signal userAuthenticated(string topic, string id, string password, string pin, string payload) signal userAuthenticationFailed(string topic, string id) signal signingResult(string topic, string id, string data) function hexToDec(hex) { return parseInt(hex, 16) } function getEstimatedTime() { return Constants.TransactionEstimatedTime.LessThanThreeMins } function convertFeesInfoToHex(feesInfo) { return null } property var mockedSuggestedFees: ({ gasPrice: 2.0, baseFee: 5.0, maxPriorityFeePerGas: 2.0, maxFeePerGasL: 1.0, maxFeePerGasM: 1.1, maxFeePerGasH: 1.2, l1GasFee: 0.0, eip1559Enabled: true }) function getSuggestedFees() { return mockedSuggestedFees } property var authenticateUserCalls: [] function authenticateUser(topic, id, address) { authenticateUserCalls.push({topic, id, address}) } property var signMessageCalls: [] function signMessage(topic, id, address, message, password, pin) { signMessageCalls.push({topic, id, address, password, pin}) } property var signMessageUnsafeCalls: [] function signMessageUnsafe(topic, id, address, data, password, pin) { signMessageUnsafeCalls.push({topic, id, address, data, password, pin}) } property var safeSignTypedDataCalls: [] function safeSignTypedData(topic, id, address, message, chainId, legacy, password, pin) { safeSignTypedDataCalls.push({topic, id, address, message, chainId, legacy, password, pin}) } property var signTransactionCalls: [] function signTransaction(topic, id, address, chainId, txObj, password, pin) { signTransactionCalls.push({topic, id, address, chainId, txObj, password, pin}) } property var sendTransactionCalls: [] function sendTransaction(topic, id, address, chainID, txObj, password, pin) { sendTransactionCalls.push({topic, id, address, chainID, txObj, password, pin}) } } dappsModel: ListModel { id: dappsModel } groupedAccountAssetsModel: ListModel { id: groupedAccountAssetsModel } networksModel: ListModel { id: networksModel ListElement { chainId: 1 layer: 1 } } accountsModel: ListModel { id: accountsModel ListElement { address: "0x123" } } currentCurrency: "USD" requests: SessionRequestsModel {} getFiatValue: (balance, cryptoSymbol) => { return parseFloat(balance) } } } TestCase { id: signRequestPluginTest property SignRequestPlugin componentUnderTest: null function populateDAppData(topic) { const dapp = { topic, name: "Example", url: "https://example.com", iconUrl: "https://example.com/icon.png", connectorId: 0, accountAddresses: [{address: "0x123"}], rawSessions: [{session: {topic}}] } componentUnderTest.dappsModel.append(dapp) } function executeRequest(signEvent) { populateDAppData(signEvent.topic) componentUnderTest.sdk.sessionRequestEvent(signEvent) // Execute the request const request = componentUnderTest.requests.get(0) request.requestItem.execute("passowrd", "pin") componentUnderTest.store.signingResult(signEvent.topic, signEvent.id, "result") compare(componentUnderTest.sdk.acceptSessionRequestCalls.length, 1, "Accept session request should be called") compare(componentUnderTest.sdk.acceptSessionRequestCalls[0].signature, "result", "Accept session request should be called with the correct signature") compare(componentUnderTest.sdk.acceptSessionRequestCalls[0].topic.toString(), signEvent.topic.toString(), "Accept session request should be called with the correct topic") compare(componentUnderTest.sdk.acceptSessionRequestCalls[0].id.toString(), signEvent.id.toString(), "Accept session request should be called with the correct id") compare(componentUnderTest.acceptedSpy.count, 1, "Accepted signal should be emitted") } function init() { componentUnderTest = createTemporaryObject(signRequestPluginComponent, root) } function test_signMessage() { const signEvent = {"id":1730896110928724,"params":{"chainId":"eip155:1","request":{"method":"personal_sign","params":["0x4578616d706c652060706572736f6e616c5f7369676e60206d657373616765","0x123","Example password"]}},"topic":"43a74a4c6c71e3ab67ef80283dc43f392445642c8dce3dabe63f89ab83cfcfc3","verifyContext":{"verified":{"origin":"https://metamask.github.io/test-dapp/","validation":"UNKNOWN","verifyUrl":"https://verify.walletconnect.org"}}} // Case 1: DApp not found ignoreWarning(`Error finding dapp for topic ${signEvent.topic} id ${signEvent.id}`) componentUnderTest.sdk.sessionRequestEvent(signEvent) compare(componentUnderTest.sdk.rejectSessionRequestCalls.length, 1, "Reject session request should not be called") // Case 2: DApp found executeRequest(signEvent) compare(componentUnderTest.store.signMessageCalls.length, 1, "Sign message should be called") componentUnderTest.sdk.sessionRequestUserAnswerResult(signEvent.topic, signEvent.id, true, null) const requestItem = componentUnderTest.requests.get(0).requestItem compare(requestItem.requestId, signEvent.id.toString(), "Request id should be set") compare(requestItem.topic, signEvent.topic.toString(), "Topic should be set") compare(requestItem.method, signEvent.params.request.method, "Method should be set") compare(requestItem.accountAddress, signEvent.params.request.params[1], "Account address should be set") compare(requestItem.chainId, signEvent.params.chainId.split(':').pop().trim(), "Chain id should be set") compare(requestItem.sourceId, componentUnderTest.dappsModel.get(0).connectorId, "Source id should be set") compare(componentUnderTest.signCompletedSpy.count, 1, "Sign completed signal should be emitted") } function test_signMessageUnsafe() { const signEvent = {"id":1730896224189361,"params":{"chainId":"eip155:1","request":{"method":"eth_sign","params":["0x123","0x123"]}},"topic":"43a74a4c6c71e3ab67ef80283dc43f392445642c8dce3dabe63f89ab83cfcfc3","verifyContext":{"verified":{"origin":"https://metamask.github.io/test-dapp/","validation":"UNKNOWN","verifyUrl":"https://verify.walletconnect.org"}}} executeRequest(signEvent) compare(componentUnderTest.store.signMessageUnsafeCalls.length, 1, "Sign message unsafe should be called") componentUnderTest.sdk.sessionRequestUserAnswerResult(signEvent.topic, signEvent.id, true, null) compare(componentUnderTest.signCompletedSpy.count, 1, "Sign completed signal should be emitted") } function test_safeSignTypedData() { const signEvent = {"id":1730896495619543,"params":{"chainId":"eip155:1","request":{"method":"eth_signTypedData_v4","params":["0x123","{\"domain\":{\"chainId\":\"1\",\"name\":\"Ether Mail\",\"verifyingContract\":\"0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC\",\"version\":\"1\"},\"message\":{\"contents\":\"Hello, Bob!\",\"from\":{\"name\":\"Cow\",\"wallets\":[\"0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826\",\"0xDeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF\"]},\"to\":[{\"name\":\"Bob\",\"wallets\":[\"0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB\",\"0xB0BdaBea57B0BDABeA57b0bdABEA57b0BDabEa57\",\"0xB0B0b0b0b0b0B000000000000000000000000000\"]}],\"attachment\":\"0x\"},\"primaryType\":\"Mail\",\"types\":{\"EIP712Domain\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"version\",\"type\":\"string\"},{\"name\":\"chainId\",\"type\":\"uint256\"},{\"name\":\"verifyingContract\",\"type\":\"address\"}],\"Group\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"members\",\"type\":\"Person[]\"}],\"Mail\":[{\"name\":\"from\",\"type\":\"Person\"},{\"name\":\"to\",\"type\":\"Person[]\"},{\"name\":\"contents\",\"type\":\"string\"},{\"name\":\"attachment\",\"type\":\"bytes\"}],\"Person\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"wallets\",\"type\":\"address[]\"}]}}"]}},"topic":"43a74a4c6c71e3ab67ef80283dc43f392445642c8dce3dabe63f89ab83cfcfc3","verifyContext":{"verified":{"origin":"https://metamask.github.io/test-dapp/","validation":"UNKNOWN","verifyUrl":"https://verify.walletconnect.org"}}} executeRequest(signEvent) compare(componentUnderTest.store.safeSignTypedDataCalls.length, 1, "Safe sign typed data should be called") componentUnderTest.sdk.sessionRequestUserAnswerResult(signEvent.topic, signEvent.id, true, null) compare(componentUnderTest.signCompletedSpy.count, 1, "Sign completed signal should be emitted") } function test_sendTransaction() { const signEvent = {"id":1730899979094571,"params":{"chainId":"eip155:1","request":{"method":"eth_sendTransaction","params":[{"from":"0x123","gasLimit":"0x5028","maxFeePerGas":"0x2540be400","maxPriorityFeePerGas":"0x3b9aca00","to":"0x0c54FcCd2e384b4BB6f2E405Bf5Cbc15a017AaFb","value":"0x0"}]}},"topic":"147fa9ddffcf9d782b5e002eb36b041e43a1db2bad79a598da6926105ce6680f","verifyContext":{"verified":{"origin":"https://metamask.github.io/test-dapp/","validation":"UNKNOWN","verifyUrl":"https://verify.walletconnect.org"}}} executeRequest(signEvent) compare(componentUnderTest.store.sendTransactionCalls.length, 1, "Send transaction should be called") componentUnderTest.sdk.sessionRequestUserAnswerResult(signEvent.topic, signEvent.id, true, null) compare(componentUnderTest.signCompletedSpy.count, 1, "Sign completed signal should be emitted") } function reject_sign() { const signEvent = {"id":1730896495619543,"params":{"chainId":"eip155:1","request":{"method":"eth_signTypedData_v4","params":["0x123","{\"domain\":{\"chainId\":\"1\",\"name\":\"Ether Mail\",\"verifyingContract\":\"0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC\",\"version\":\"1\"},\"message\":{\"contents\":\"Hello, Bob!\",\"from\":{\"name\":\"Cow\",\"wallets\":[\"0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826\",\"0xDeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF\"]},\"to\":[{\"name\":\"Bob\",\"wallets\":[\"0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB\",\"0xB0BdaBea57B0BDABeA57b0bdABEA57b0BDabEa57\",\"0xB0B0b0b0b0b0B000000000000000000000000000\"]}],\"attachment\":\"0x\"},\"primaryType\":\"Mail\",\"types\":{\"EIP712Domain\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"version\",\"type\":\"string\"},{\"name\":\"chainId\",\"type\":\"uint256\"},{\"name\":\"verifyingContract\",\"type\":\"address\"}],\"Group\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"members\",\"type\":\"Person[]\"}],\"Mail\":[{\"name\":\"from\",\"type\":\"Person\"},{\"name\":\"to\",\"type\":\"Person[]\"},{\"name\":\"contents\",\"type\":\"string\"},{\"name\":\"attachment\",\"type\":\"bytes\"}],\"Person\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"wallets\",\"type\":\"address[]\"}]}}"]}},"topic":"43a74a4c6c71e3ab67ef80283dc43f392445642c8dce3dabe63f89ab83cfcfc3","verifyContext":{"verified":{"origin":"https://metamask.github.io/test-dapp/","validation":"UNKNOWN","verifyUrl":"https://verify.walletconnect.org"}}} populateDAppData(signEvent.topic) componentUnderTest.sdk.sessionRequestEvent(signEvent) // Execute the request const request = componentUnderTest.requests.get(0) request.requestItem.rejected(false) compare(componentUnderTest.sdk.rejectSessionRequestCalls.length, 1, "Reject session request should be called") compare(componentUnderTest.rejectedSpy.count, 1, "Rejected signal should be emitted") compare(componentUnderTest.signCompletedSpy.count, 0, "Sign completed signal should not be emitted") } function test_authFailed() { const signEvent = {"id":1730896495619543,"params":{"chainId":"eip155:1","request":{"method":"eth_signTypedData_v4","params":["0x123","{\"domain\":{\"chainId\":\"1\",\"name\":\"Ether Mail\",\"verifyingContract\":\"0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC\",\"version\":\"1\"},\"message\":{\"contents\":\"Hello, Bob!\",\"from\":{\"name\":\"Cow\",\"wallets\":[\"0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826\",\"0xDeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF\"]},\"to\":[{\"name\":\"Bob\",\"wallets\":[\"0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB\",\"0xB0BdaBea57B0BDABeA57b0bdABEA57b0BDabEa57\",\"0xB0B0b0b0b0b0B000000000000000000000000000\"]}],\"attachment\":\"0x\"},\"primaryType\":\"Mail\",\"types\":{\"EIP712Domain\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"version\",\"type\":\"string\"},{\"name\":\"chainId\",\"type\":\"uint256\"},{\"name\":\"verifyingContract\",\"type\":\"address\"}],\"Group\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"members\",\"type\":\"Person[]\"}],\"Mail\":[{\"name\":\"from\",\"type\":\"Person\"},{\"name\":\"to\",\"type\":\"Person[]\"},{\"name\":\"contents\",\"type\":\"string\"},{\"name\":\"attachment\",\"type\":\"bytes\"}],\"Person\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"wallets\",\"type\":\"address[]\"}]}}"]}},"topic":"43a74a4c6c71e3ab67ef80283dc43f392445642c8dce3dabe63f89ab83cfcfc3","verifyContext":{"verified":{"origin":"https://metamask.github.io/test-dapp/","validation":"UNKNOWN","verifyUrl":"https://verify.walletconnect.org"}}} populateDAppData(signEvent.topic) componentUnderTest.sdk.sessionRequestEvent(signEvent) // Execute the request const request = componentUnderTest.requests.get(0) request.requestItem.authFailed() compare(componentUnderTest.sdk.rejectSessionRequestCalls.length, 1, "Reject session request should be called") compare(componentUnderTest.rejectedSpy.count, 1, "Rejected signal should be emitted") compare(componentUnderTest.signCompletedSpy.count, 0, "Sign completed signal should not be emitted") } function test_signMessageFails() { const signEvent = {"id":1730896495619543,"params":{"chainId":"eip155:1","request":{"method":"eth_signTypedData_v4","params":["0x123","{\"domain\":{\"chainId\":\"1\",\"name\":\"Ether Mail\",\"verifyingContract\":\"0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC\",\"version\":\"1\"},\"message\":{\"contents\":\"Hello, Bob!\",\"from\":{\"name\":\"Cow\",\"wallets\":[\"0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826\",\"0xDeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF\"]},\"to\":[{\"name\":\"Bob\",\"wallets\":[\"0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB\",\"0xB0BdaBea57B0BDABeA57b0bdABEA57b0BDabEa57\",\"0xB0B0b0b0b0b0B000000000000000000000000000\"]}],\"attachment\":\"0x\"},\"primaryType\":\"Mail\",\"types\":{\"EIP712Domain\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"version\",\"type\":\"string\"},{\"name\":\"chainId\",\"type\":\"uint256\"},{\"name\":\"verifyingContract\",\"type\":\"address\"}],\"Group\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"members\",\"type\":\"Person[]\"}],\"Mail\":[{\"name\":\"from\",\"type\":\"Person\"},{\"name\":\"to\",\"type\":\"Person[]\"},{\"name\":\"contents\",\"type\":\"string\"},{\"name\":\"attachment\",\"type\":\"bytes\"}],\"Person\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"wallets\",\"type\":\"address[]\"}]}}"]}},"topic":"43a74a4c6c71e3ab67ef80283dc43f392445642c8dce3dabe63f89ab83cfcfc3","verifyContext":{"verified":{"origin":"https://metamask.github.io/test-dapp/","validation":"UNKNOWN","verifyUrl":"https://verify.walletconnect.org"}}} populateDAppData(signEvent.topic) componentUnderTest.sdk.sessionRequestEvent(signEvent) // Execute the request const request = componentUnderTest.requests.get(0) request.requestItem.execute("passowrd", "pin") componentUnderTest.store.signingResult(signEvent.topic, signEvent.id, "") compare(componentUnderTest.sdk.rejectSessionRequestCalls.length, 1, "Reject session request should be called") compare(componentUnderTest.rejectedSpy.count, 1, "Rejected signal should be emitted") compare(componentUnderTest.signCompletedSpy.count, 0, "Sign completed signal should not be emitted") } } }