From ffdc0673605f5a6987de4b50aa8f3cd0c5c98353 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Thu, 28 Mar 2024 11:32:23 +0100 Subject: [PATCH 1/2] savepoint --- .../Sign/SignClientTests.swift | 69 +++++++++++++++++++ .../Services/SessionNamespaceBuilder.swift | 22 ++++-- .../SessionNamespaceBuilderTests.swift | 27 +++++++- 3 files changed, 111 insertions(+), 7 deletions(-) diff --git a/Example/IntegrationTests/Sign/SignClientTests.swift b/Example/IntegrationTests/Sign/SignClientTests.swift index e67e77973..c695a9365 100644 --- a/Example/IntegrationTests/Sign/SignClientTests.swift +++ b/Example/IntegrationTests/Sign/SignClientTests.swift @@ -1036,6 +1036,75 @@ final class SignClientTests: XCTestCase { await fulfillment(of: [requestExpectation, responseExpectation], timeout: InputConfig.defaultTimeout) } + + func testSessionRequestOnAuthenticatedSessionForAChainNotIncludedInCacao() async throws { + let requestExpectation = expectation(description: "Wallet expects to receive a request") + let responseExpectation = expectation(description: "Dapp expects to receive a response") + + let requestMethod = "eth_sendTransaction" + let requestParams = [EthSendTransaction.stub()] + let responseParams = "0xdeadbeef" + let chain = Blockchain("eip155:1")! + // sleep is needed as emitRequestIfPending() will be called on client init and then on request itself, second request would be debouced + sleep(1) + wallet.authenticateRequestPublisher.sink { [unowned self] (request, _) in + Task(priority: .high) { + let signerFactory = DefaultSignerFactory() + let signer = MessageSignerFactory(signerFactory: signerFactory).create() + + let supportedAuthPayload = try! wallet.buildAuthPayload(payload: request.payload, supportedEVMChains: [Blockchain("eip155:1")!, Blockchain("eip155:137")!], supportedMethods: ["eth_sendTransaction", "personal_sign"]) + + let signingAccount = Account(chainIdentifier: "eip155:1", address: "0x724d0D2DaD3fbB0C168f947B87Fa5DBe36F1A8bf")! + let siweMessage = try! wallet.formatAuthMessage(payload: supportedAuthPayload, account: signingAccount) + + let signature = try! signer.sign( + message: siweMessage, + privateKey: prvKey, + type: .eip191) + + let cacao = try! wallet.buildSignedAuthObject(authPayload: supportedAuthPayload, signature: signature, account: walletAccount) + + _ = try! await wallet.approveSessionAuthenticate(requestId: request.id, auths: [cacao]) + } + } + .store(in: &publishers) + dapp.authResponsePublisher.sink { [unowned self] (_, result) in + guard case .success(let (session, _)) = result, + let session = session else { XCTFail(); return } + Task(priority: .high) { + let request = try Request(id: RPCID(0), topic: session.topic, method: requestMethod, params: requestParams, chainId: Blockchain("eip155:137")!) + try await dapp.request(params: request) + } + } + .store(in: &publishers) + + wallet.sessionRequestPublisher.sink { [unowned self] (sessionRequest, _) in + let receivedParams = try! sessionRequest.params.get([EthSendTransaction].self) + XCTAssertEqual(receivedParams, requestParams) + XCTAssertEqual(sessionRequest.method, requestMethod) + requestExpectation.fulfill() + Task(priority: .high) { + try await wallet.respond(topic: sessionRequest.topic, requestId: sessionRequest.id, response: .response(AnyCodable(responseParams))) + } + }.store(in: &publishers) + + dapp.sessionResponsePublisher.sink { response in + switch response.result { + case .response(let response): + XCTAssertEqual(try! response.get(String.self), responseParams) + case .error: + XCTFail() + } + responseExpectation.fulfill() + }.store(in: &publishers) + + + let uri = try await dapp.authenticate(AuthRequestParams.stub(chains: ["eip155:1", "eip155:137"])) + + try await walletPairingClient.pair(uri: uri) + await fulfillment(of: [requestExpectation, responseExpectation], timeout: InputConfig.defaultTimeout) + } + func testFalbackForm_2_5_DappToSessionProposeOnWallet() async throws { let fallbackExpectation = expectation(description: "fallback to wc_sessionPropose") diff --git a/Sources/WalletConnectSign/Auth/Services/SessionNamespaceBuilder.swift b/Sources/WalletConnectSign/Auth/Services/SessionNamespaceBuilder.swift index ddb897788..3e1931910 100644 --- a/Sources/WalletConnectSign/Auth/Services/SessionNamespaceBuilder.swift +++ b/Sources/WalletConnectSign/Auth/Services/SessionNamespaceBuilder.swift @@ -33,17 +33,27 @@ class SessionNamespaceBuilder { throw Errors.cannotCreateSessionNamespaceFromTheRecap } - let accounts = cacaos.compactMap { try? DIDPKH(did: $0.p.iss).account } - let accountsSet = accounts - let methods = firstRecapResource.methods let chains = firstRecapResource.chains - let events: Set = ["chainChanged", "accountsChanged"] - guard !chains.isEmpty else { throw Errors.cannotCreateSessionNamespaceFromTheRecap } - let sessionNamespace = SessionNamespace(chains: chains, accounts: accountsSet, methods: methods, events: events) + let addresses = Set(cacaos.compactMap { try? DIDPKH(did: $0.p.iss).account.address }) + var accounts = [Account]() + + for address in addresses { + for chain in chains { + if let account = Account(blockchain: chain, address: address) { + accounts.append(account) + } + } + } + + let methods = firstRecapResource.methods + let events: Set = ["chainChanged", "accountsChanged"] + + let sessionNamespace = SessionNamespace(chains: chains, accounts: accounts, methods: methods, events: events) return [chainsNamespace: sessionNamespace] } + } diff --git a/Tests/WalletConnectSignTests/SessionNamespaceBuilderTests.swift b/Tests/WalletConnectSignTests/SessionNamespaceBuilderTests.swift index 21928efc4..269732c3b 100644 --- a/Tests/WalletConnectSignTests/SessionNamespaceBuilderTests.swift +++ b/Tests/WalletConnectSignTests/SessionNamespaceBuilderTests.swift @@ -37,6 +37,30 @@ class SessionNamespaceBuilderTests: XCTestCase { super.tearDown() } + func testBuildSessionNamespaces_ValidOneCacao_ReturnsExpectedNamespaceWithMultipleAccounts() { + let expectedSessionNamespace = SessionNamespace( + chains: [Blockchain("eip155:1")!, Blockchain("eip155:137")!], + accounts: [ + Account("eip155:1:0x000a10343Bcdebe21283c7172d67a9a113E819C5")!, + Account("eip155:137:0x000a10343Bcdebe21283c7172d67a9a113E819C5")! + ], + methods: Set(["personal_sign", "eth_signTypedData", "eth_sign"]), + events: Set(["chainChanged", "accountsChanged"]) + ) + + let cacaos = [ + Cacao.stub(account: Account("eip155:1:0x000a10343Bcdebe21283c7172d67a9a113E819C5")!, resources: [recapUrn]), + ] + + do { + let namespaces = try sessionNamespaceBuilder.buildSessionNamespaces(cacaos: cacaos) + XCTAssertTrue(namespaces.first!.value.events.isSuperset(of: ["chainChanged", "accountsChanged"]), "Contains required events") + XCTAssertEqual(namespaces.count, 1, "There should be one namespace") + XCTAssertEqual(expectedSessionNamespace, namespaces.first!.value, "The namespace is equal to the expected one") + } catch { + XCTFail("Expected successful namespace creation, but received error: \(error)") + } + } func testBuildSessionNamespaces_ValidCacaos_ReturnsExpectedNamespace() { let expectedSessionNamespace = SessionNamespace( @@ -69,7 +93,8 @@ class SessionNamespaceBuilderTests: XCTestCase { let expectedSessionNamespace = SessionNamespace( chains: [Blockchain("eip155:1")!, Blockchain("eip155:137")!], accounts: [ - Account("eip155:1:0x000a10343Bcdebe21283c7172d67a9a113E819C5")! + Account("eip155:1:0x000a10343Bcdebe21283c7172d67a9a113E819C5")!, + Account("eip155:137:0x000a10343Bcdebe21283c7172d67a9a113E819C5")! ], methods: ["personal_sign", "eth_signTypedData", "eth_sign"], events: ["chainChanged", "accountsChanged"] From c00980277b2f58aaa037320dc1088ba4acfb1803 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Thu, 28 Mar 2024 11:40:44 +0100 Subject: [PATCH 2/2] add test --- .../SessionNamespaceBuilderTests.swift | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/Tests/WalletConnectSignTests/SessionNamespaceBuilderTests.swift b/Tests/WalletConnectSignTests/SessionNamespaceBuilderTests.swift index 269732c3b..d441cafb1 100644 --- a/Tests/WalletConnectSignTests/SessionNamespaceBuilderTests.swift +++ b/Tests/WalletConnectSignTests/SessionNamespaceBuilderTests.swift @@ -62,6 +62,34 @@ class SessionNamespaceBuilderTests: XCTestCase { } } + func testBuildSessionNamespaces_ValidOneCacaos_ReturnsExpectedNamespaceWithMultipleAccountsForDifferentAddresses() { + let expectedSessionNamespace = SessionNamespace( + chains: [Blockchain("eip155:1")!, Blockchain("eip155:137")!], + accounts: [ + Account("eip155:1:0x990a10343Bcdebe21283c7172d67a9a113E819X5")!, + Account("eip155:137:0x990a10343Bcdebe21283c7172d67a9a113E819X5")!, + Account("eip155:1:0x000a10343Bcdebe21283c7172d67a9a113E819C5")!, + Account("eip155:137:0x000a10343Bcdebe21283c7172d67a9a113E819C5")! + ], + methods: Set(["personal_sign", "eth_signTypedData", "eth_sign"]), + events: Set(["chainChanged", "accountsChanged"]) + ) + + let cacaos = [ + Cacao.stub(account: Account("eip155:1:0x000a10343Bcdebe21283c7172d67a9a113E819C5")!, resources: [recapUrn]), + Cacao.stub(account: Account("eip155:1:0x990a10343Bcdebe21283c7172d67a9a113E819X5")!, resources: [recapUrn]) + ] + + do { + let namespaces = try sessionNamespaceBuilder.buildSessionNamespaces(cacaos: cacaos) + XCTAssertTrue(namespaces.first!.value.events.isSuperset(of: ["chainChanged", "accountsChanged"]), "Contains required events") + XCTAssertEqual(namespaces.count, 1, "There should be one namespace") + XCTAssertEqual(expectedSessionNamespace, namespaces.first!.value, "The namespace is equal to the expected one") + } catch { + XCTFail("Expected successful namespace creation, but received error: \(error)") + } + } + func testBuildSessionNamespaces_ValidCacaos_ReturnsExpectedNamespace() { let expectedSessionNamespace = SessionNamespace( chains: [Blockchain("eip155:1")!, Blockchain("eip155:137")!],