diff --git a/Example/IntegrationTests/Auth/AuthTests.swift b/Example/IntegrationTests/Auth/AuthTests.swift index 90f110912..5ab58d0ad 100644 --- a/Example/IntegrationTests/Auth/AuthTests.swift +++ b/Example/IntegrationTests/Auth/AuthTests.swift @@ -76,7 +76,7 @@ final class AuthTests: XCTestCase { let uri = try! await appPairingClient.create() try! await appAuthClient.request(RequestParams.stub(), topic: uri.topic) - try! await walletPairingClient.pair(uri: uri) + try? await walletPairingClient.pair(uri: uri) walletAuthClient.authRequestPublisher.sink { _ in requestExpectation.fulfill() }.store(in: &publishers) @@ -88,7 +88,7 @@ final class AuthTests: XCTestCase { let uri = try! await appPairingClient.create() try! await appAuthClient.request(RequestParams.stub(), topic: uri.topic) - try! await walletPairingClient.pair(uri: uri) + try? await walletPairingClient.pair(uri: uri) walletAuthClient.authRequestPublisher.sink { [unowned self] request in Task(priority: .high) { let signerFactory = DefaultSignerFactory() @@ -126,7 +126,7 @@ final class AuthTests: XCTestCase { resources: nil ), topic: uri.topic) - try! await walletPairingClient.pair(uri: uri) + try? await walletPairingClient.pair(uri: uri) walletAuthClient.authRequestPublisher.sink { [unowned self] request in Task(priority: .high) { let signature = CacaoSignature(t: .eip1271, s: eip1271Signature) @@ -147,7 +147,7 @@ final class AuthTests: XCTestCase { let uri = try! await appPairingClient.create() try! await appAuthClient.request(RequestParams.stub(), topic: uri.topic) - try! await walletPairingClient.pair(uri: uri) + try? await walletPairingClient.pair(uri: uri) walletAuthClient.authRequestPublisher.sink { [unowned self] request in Task(priority: .high) { let signature = CacaoSignature(t: .eip1271, s: eip1271Signature) @@ -168,7 +168,7 @@ final class AuthTests: XCTestCase { let uri = try! await appPairingClient.create() try! await appAuthClient.request(RequestParams.stub(), topic: uri.topic) - try! await walletPairingClient.pair(uri: uri) + try? await walletPairingClient.pair(uri: uri) walletAuthClient.authRequestPublisher.sink { [unowned self] request in Task(priority: .high) { try! await walletAuthClient.reject(requestId: request.0.id) @@ -189,7 +189,7 @@ final class AuthTests: XCTestCase { let uri = try! await appPairingClient.create() try! await appAuthClient.request(RequestParams.stub(), topic: uri.topic) - try! await walletPairingClient.pair(uri: uri) + try? await walletPairingClient.pair(uri: uri) walletAuthClient.authRequestPublisher.sink { [unowned self] request in Task(priority: .high) { let invalidSignature = "438effc459956b57fcd9f3dac6c675f9cee88abf21acab7305e8e32aa0303a883b06dcbd956279a7a2ca21ffa882ff55cc22e8ab8ec0f3fe90ab45f306938cfa1b" diff --git a/Example/IntegrationTests/Pairing/PairingTests.swift b/Example/IntegrationTests/Pairing/PairingTests.swift index 6d8005a6c..a2b49e07b 100644 --- a/Example/IntegrationTests/Pairing/PairingTests.swift +++ b/Example/IntegrationTests/Pairing/PairingTests.swift @@ -110,7 +110,7 @@ final class PairingTests: XCTestCase { let expectation = expectation(description: "expects ping response") makeWalletClients() let uri = try! await appPairingClient.create() - try! await walletPairingClient.pair(uri: uri) + try? await walletPairingClient.pair(uri: uri) try! await walletPairingClient.ping(topic: uri.topic) walletPairingClient.pingResponsePublisher .sink { topic in @@ -131,7 +131,7 @@ final class PairingTests: XCTestCase { let uri = try! await appPairingClient.create() - try! await walletPairingClient.pair(uri: uri) + try? await walletPairingClient.pair(uri: uri) try! await appAuthClient.request(RequestParams.stub(), topic: uri.topic) diff --git a/Example/WalletApp/PresentationLayer/Wallet/SessionProposal/SessionProposalInteractor.swift b/Example/WalletApp/PresentationLayer/Wallet/SessionProposal/SessionProposalInteractor.swift index 226f5b80d..0216a3db6 100644 --- a/Example/WalletApp/PresentationLayer/Wallet/SessionProposal/SessionProposalInteractor.swift +++ b/Example/WalletApp/PresentationLayer/Wallet/SessionProposal/SessionProposalInteractor.swift @@ -20,18 +20,14 @@ final class SessionProposalInteractor { let supportedChains = [Blockchain("eip155:1")!, Blockchain("eip155:137")!] let supportedAccounts = [Account(blockchain: Blockchain("eip155:1")!, address: ETHSigner.address)!, Account(blockchain: Blockchain("eip155:137")!, address: ETHSigner.address)!] */ - do { - let sessionNamespaces = try AutoNamespaces.build( - sessionProposal: proposal, - chains: Array(supportedChains), - methods: Array(supportedMethods), - events: Array(supportedEvents), - accounts: supportedAccounts - ) - try await Web3Wallet.instance.approve(proposalId: proposal.id, namespaces: sessionNamespaces, sessionProperties: proposal.sessionProperties) - } catch { - print(error) - } + let sessionNamespaces = try AutoNamespaces.build( + sessionProposal: proposal, + chains: Array(supportedChains), + methods: Array(supportedMethods), + events: Array(supportedEvents), + accounts: supportedAccounts + ) + try await Web3Wallet.instance.approve(proposalId: proposal.id, namespaces: sessionNamespaces, sessionProperties: proposal.sessionProperties) } func reject(proposal: Session.Proposal) async throws { diff --git a/Example/WalletApp/PresentationLayer/Wallet/SessionProposal/SessionProposalPresenter.swift b/Example/WalletApp/PresentationLayer/Wallet/SessionProposal/SessionProposalPresenter.swift index 117db225c..ad02dd7a4 100644 --- a/Example/WalletApp/PresentationLayer/Wallet/SessionProposal/SessionProposalPresenter.swift +++ b/Example/WalletApp/PresentationLayer/Wallet/SessionProposal/SessionProposalPresenter.swift @@ -11,6 +11,9 @@ final class SessionProposalPresenter: ObservableObject { let sessionProposal: Session.Proposal let verified: Bool? + @Published var showError = false + @Published var errorMessage = "Error" + private var disposeBag = Set() init( @@ -30,14 +33,24 @@ final class SessionProposalPresenter: ObservableObject { @MainActor func onApprove() async throws { - try await interactor.approve(proposal: sessionProposal, account: importAccount.account) - router.dismiss() + do { + try await interactor.approve(proposal: sessionProposal, account: importAccount.account) + router.dismiss() + } catch { + errorMessage = error.localizedDescription + showError.toggle() + } } @MainActor func onReject() async throws { - try await interactor.reject(proposal: sessionProposal) - router.dismiss() + do { + try await interactor.reject(proposal: sessionProposal) + router.dismiss() + } catch { + errorMessage = error.localizedDescription + showError.toggle() + } } } diff --git a/Example/WalletApp/PresentationLayer/Wallet/SessionProposal/SessionProposalView.swift b/Example/WalletApp/PresentationLayer/Wallet/SessionProposal/SessionProposalView.swift index 97717d9b5..8082ce1ee 100644 --- a/Example/WalletApp/PresentationLayer/Wallet/SessionProposal/SessionProposalView.swift +++ b/Example/WalletApp/PresentationLayer/Wallet/SessionProposal/SessionProposalView.swift @@ -146,6 +146,9 @@ struct SessionProposalView: View { Spacer() } } + .alert(presenter.errorMessage, isPresented: $presenter.showError) { + Button("OK", role: .cancel) {} + } .edgesIgnoringSafeArea(.all) } //private func sessionProposalView(chain: String) -> some View { diff --git a/Example/WalletApp/PresentationLayer/Wallet/Wallet/WalletPresenter.swift b/Example/WalletApp/PresentationLayer/Wallet/Wallet/WalletPresenter.swift index 917d1dc94..c8a5baee1 100644 --- a/Example/WalletApp/PresentationLayer/Wallet/Wallet/WalletPresenter.swift +++ b/Example/WalletApp/PresentationLayer/Wallet/Wallet/WalletPresenter.swift @@ -40,12 +40,7 @@ final class WalletPresenter: ObservableObject { func onAppear() { showPairingLoading = app.requestSent removePairingIndicator() - - let proposals = interactor.getPendingProposals() - if let proposal = proposals.last { - router.present(sessionProposal: proposal.proposal, importAccount: importAccount, sessionContext: proposal.context) - } - + let pendingRequests = interactor.getPendingRequests() if let request = pendingRequests.first(where: { $0.context != nil }) { router.present(sessionRequest: request.request, importAccount: importAccount, sessionContext: request.context) diff --git a/Sources/Auth/AuthClientFactory.swift b/Sources/Auth/AuthClientFactory.swift index 1faf925ff..dbddf2a53 100644 --- a/Sources/Auth/AuthClientFactory.swift +++ b/Sources/Auth/AuthClientFactory.swift @@ -48,7 +48,7 @@ public struct AuthClientFactory { let appRespondSubscriber = AppRespondSubscriber(networkingInteractor: networkingClient, logger: logger, rpcHistory: history, signatureVerifier: signatureVerifier, pairingRegisterer: pairingRegisterer, messageFormatter: messageFormatter) let walletErrorResponder = WalletErrorResponder(networkingInteractor: networkingClient, logger: logger, kms: kms, rpcHistory: history) let walletRequestSubscriber = WalletRequestSubscriber(networkingInteractor: networkingClient, logger: logger, kms: kms, walletErrorResponder: walletErrorResponder, pairingRegisterer: pairingRegisterer, verifyClient: verifyClient, verifyContextStore: verifyContextStore) - let walletRespondService = WalletRespondService(networkingInteractor: networkingClient, logger: logger, kms: kms, rpcHistory: history, verifyContextStore: verifyContextStore, walletErrorResponder: walletErrorResponder) + let walletRespondService = WalletRespondService(networkingInteractor: networkingClient, logger: logger, kms: kms, rpcHistory: history, verifyContextStore: verifyContextStore, walletErrorResponder: walletErrorResponder, pairingRegisterer: pairingRegisterer) let pendingRequestsProvider = PendingRequestsProvider(rpcHistory: history, verifyContextStore: verifyContextStore) return AuthClient( diff --git a/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift b/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift index 02ee542d9..c2a7030c0 100644 --- a/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift +++ b/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift @@ -37,10 +37,7 @@ class WalletRequestSubscriber { .sink { [unowned self] (payload: RequestSubscriptionPayload) in logger.debug("WalletRequestSubscriber: Received request") - pairingRegisterer.activate( - pairingTopic: payload.topic, - peerMetadata: payload.request.requester.metadata - ) + pairingRegisterer.setReceived(pairingTopic: payload.topic) let request = AuthRequest(id: payload.id, topic: payload.topic, payload: payload.request.payloadParams) diff --git a/Sources/Auth/Services/Wallet/WalletRespondService.swift b/Sources/Auth/Services/Wallet/WalletRespondService.swift index 8d5e78b0c..a06a16027 100644 --- a/Sources/Auth/Services/Wallet/WalletRespondService.swift +++ b/Sources/Auth/Services/Wallet/WalletRespondService.swift @@ -11,19 +11,24 @@ actor WalletRespondService { private let verifyContextStore: CodableStore private let logger: ConsoleLogging private let walletErrorResponder: WalletErrorResponder + private let pairingRegisterer: PairingRegisterer - init(networkingInteractor: NetworkInteracting, - logger: ConsoleLogging, - kms: KeyManagementService, - rpcHistory: RPCHistory, - verifyContextStore: CodableStore, - walletErrorResponder: WalletErrorResponder) { + init( + networkingInteractor: NetworkInteracting, + logger: ConsoleLogging, + kms: KeyManagementService, + rpcHistory: RPCHistory, + verifyContextStore: CodableStore, + walletErrorResponder: WalletErrorResponder, + pairingRegisterer: PairingRegisterer + ) { self.networkingInteractor = networkingInteractor self.logger = logger self.kms = kms self.rpcHistory = rpcHistory self.verifyContextStore = verifyContextStore self.walletErrorResponder = walletErrorResponder + self.pairingRegisterer = pairingRegisterer } func respond(requestId: RPCID, signature: CacaoSignature, account: Account) async throws { @@ -34,10 +39,16 @@ actor WalletRespondService { let header = CacaoHeader(t: "eip4361") let payload = try authRequestParams.payloadParams.cacaoPayload(address: account.address) - let responseParams = AuthResponseParams(h: header, p: payload, s: signature) + let responseParams = AuthResponseParams(h: header, p: payload, s: signature) let response = RPCResponse(id: requestId, result: responseParams) try await networkingInteractor.respond(topic: topic, response: response, protocolMethod: AuthRequestProtocolMethod(), envelopeType: .type1(pubKey: keys.publicKey.rawRepresentation)) + + pairingRegisterer.activate( + pairingTopic: topic, + peerMetadata: authRequestParams.requester.metadata + ) + verifyContextStore.delete(forKey: requestId.string) } diff --git a/Sources/WalletConnectNetworking/NetworkInteracting.swift b/Sources/WalletConnectNetworking/NetworkInteracting.swift index 507e82603..54987272c 100644 --- a/Sources/WalletConnectNetworking/NetworkInteracting.swift +++ b/Sources/WalletConnectNetworking/NetworkInteracting.swift @@ -13,7 +13,8 @@ public protocol NetworkInteracting { func respond(topic: String, response: RPCResponse, protocolMethod: ProtocolMethod, envelopeType: Envelope.EnvelopeType) async throws func respondSuccess(topic: String, requestId: RPCID, protocolMethod: ProtocolMethod, envelopeType: Envelope.EnvelopeType) async throws func respondError(topic: String, requestId: RPCID, protocolMethod: ProtocolMethod, reason: Reason, envelopeType: Envelope.EnvelopeType) async throws - + func handleHistoryRequest(topic: String, request: RPCRequest) + func requestSubscription( on request: ProtocolMethod ) -> AnyPublisher, Never> diff --git a/Sources/WalletConnectNetworking/NetworkingInteractor.swift b/Sources/WalletConnectNetworking/NetworkingInteractor.swift index d216f9974..e92cfa3ea 100644 --- a/Sources/WalletConnectNetworking/NetworkingInteractor.swift +++ b/Sources/WalletConnectNetworking/NetworkingInteractor.swift @@ -162,6 +162,10 @@ public class NetworkingInteractor: NetworkInteracting { logger.debug("Networking Interactor - Received unknown object type from networking relay") } } + + public func handleHistoryRequest(topic: String, request: RPCRequest) { + requestPublisherSubject.send((topic, request, Data(), Date(), nil)) + } private func handleRequest(topic: String, request: RPCRequest, decryptedPayload: Data, publishedAt: Date, derivedTopic: String?) { do { diff --git a/Sources/WalletConnectPairing/PairingClientFactory.swift b/Sources/WalletConnectPairing/PairingClientFactory.swift index 9706702af..ba49bb6af 100644 --- a/Sources/WalletConnectPairing/PairingClientFactory.swift +++ b/Sources/WalletConnectPairing/PairingClientFactory.swift @@ -12,8 +12,9 @@ public struct PairingClientFactory { public static func create(logger: ConsoleLogging, keyValueStorage: KeyValueStorage, keychainStorage: KeychainStorageProtocol, networkingClient: NetworkingInteractor) -> PairingClient { let pairingStore = PairingStorage(storage: SequenceStore(store: .init(defaults: keyValueStorage, identifier: PairStorageIdentifiers.pairings.rawValue))) let kms = KeyManagementService(keychain: keychainStorage) + let history = RPCHistoryFactory.createForNetwork(keyValueStorage: keyValueStorage) let appPairService = AppPairService(networkingInteractor: networkingClient, kms: kms, pairingStorage: pairingStore) - let walletPairService = WalletPairService(networkingInteractor: networkingClient, kms: kms, pairingStorage: pairingStore) + let walletPairService = WalletPairService(networkingInteractor: networkingClient, kms: kms, pairingStorage: pairingStore, history: history) let pairingRequestsSubscriber = PairingRequestsSubscriber(networkingInteractor: networkingClient, pairingStorage: pairingStore, logger: logger) let pairingsProvider = PairingsProvider(pairingStorage: pairingStore) let cleanupService = PairingCleanupService(pairingStore: pairingStore, kms: kms) diff --git a/Sources/WalletConnectPairing/Services/Wallet/WalletPairService.swift b/Sources/WalletConnectPairing/Services/Wallet/WalletPairService.swift index d14d82e3c..c962b8a41 100644 --- a/Sources/WalletConnectPairing/Services/Wallet/WalletPairService.swift +++ b/Sources/WalletConnectPairing/Services/Wallet/WalletPairService.swift @@ -9,18 +9,23 @@ actor WalletPairService { let networkingInteractor: NetworkInteracting let kms: KeyManagementServiceProtocol private let pairingStorage: WCPairingStorage + private let history: RPCHistory - init(networkingInteractor: NetworkInteracting, - kms: KeyManagementServiceProtocol, - pairingStorage: WCPairingStorage) { + init( + networkingInteractor: NetworkInteracting, + kms: KeyManagementServiceProtocol, + pairingStorage: WCPairingStorage, + history: RPCHistory + ) { self.networkingInteractor = networkingInteractor self.kms = kms self.pairingStorage = pairingStorage + self.history = history } func pair(_ uri: WalletConnectURI) async throws { - guard !hasPairing(for: uri.topic) else { - throw Errors.pairingAlreadyExist(topic: uri.topic) + guard try !pairingHasPendingRequest(for: uri.topic) else { + return } let pairing = WCPairing(uri: uri) @@ -39,9 +44,23 @@ actor WalletPairService { // MARK: - Private functions extension WalletPairService { - func hasPairing(for topic: String) -> Bool { - if let pairing = pairingStorage.getPairing(forTopic: topic) { - return pairing.requestReceived + func pairingHasPendingRequest(for topic: String) throws -> Bool { + guard let pairing = pairingStorage.getPairing(forTopic: topic), pairing.requestReceived else { + return false + } + + if pairing.active { + throw Errors.pairingAlreadyExist(topic: topic) + } + + let pendingRequests = history.getPending() + .compactMap { record -> RPCRequest? in + (record.topic == pairing.topic) ? record.request : nil + } + + if let pendingRequest = pendingRequests.first { + networkingInteractor.handleHistoryRequest(topic: topic, request: pendingRequest) + return true } return false } @@ -65,7 +84,7 @@ extension WalletPairService { extension WalletPairService.Errors: LocalizedError { var errorDescription: String? { switch self { - case .pairingAlreadyExist(let topic): return "Pairing with topic (\(topic)) already exists. Use 'Web3Wallet.instance.getPendingProposals()' or 'Web3Wallet.instance.getPendingRequests()' in order to receive proposals or requests." + case .pairingAlreadyExist(let topic): return "Pairing with topic (\(topic)) is already active" case .networkNotConnected: return "Pairing failed. You seem to be offline" } } diff --git a/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift b/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift index 3071e12e6..dd77da949 100644 --- a/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift +++ b/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift @@ -9,6 +9,7 @@ final class ApproveEngine { case pairingNotFound case sessionNotFound case agreementMissingOrInvalid + case networkNotConnected } var onSessionProposal: ((Session.Proposal, VerifyContext?) -> Void)? @@ -63,6 +64,11 @@ final class ApproveEngine { guard let payload = try proposalPayloadsStore.get(key: proposerPubKey) else { throw Errors.wrongRequestParams } + + let networkConnectionStatus = await resolveNetworkConnectionStatus() + guard networkConnectionStatus == .connected else { + throw Errors.networkNotConnected + } let proposal = payload.request let pairingTopic = payload.topic @@ -300,6 +306,11 @@ private extension ApproveEngine { pairingRegisterer.setReceived(pairingTopic: payload.topic) + if let verifyContext = try? verifyContextStore.get(key: proposal.proposer.publicKey) { + onSessionProposal?(proposal.publicRepresentation(pairingTopic: payload.topic), verifyContext) + return + } + Task(priority: .high) { let assertionId = payload.decryptedPayload.sha256().toHexString() do { @@ -373,4 +384,28 @@ private extension ApproveEngine { } onSessionSettle?(session.publicRepresentation()) } + + func resolveNetworkConnectionStatus() async -> NetworkConnectionStatus { + return await withCheckedContinuation { continuation in + let cancellable = networkingInteractor.networkConnectionStatusPublisher.sink { value in + continuation.resume(returning: value) + } + + Task(priority: .high) { + await withTaskCancellationHandler { + cancellable.cancel() + } onCancel: { } + } + } + } +} + +// MARK: - LocalizedError +extension ApproveEngine.Errors: LocalizedError { + var errorDescription: String? { + switch self { + case .networkNotConnected: return "Action failed. You seem to be offline" + default: return "" + } + } } diff --git a/Tests/AuthTests/WalletRequestSubscriberTests.swift b/Tests/AuthTests/WalletRequestSubscriberTests.swift index 2508ae587..1fba88977 100644 --- a/Tests/AuthTests/WalletRequestSubscriberTests.swift +++ b/Tests/AuthTests/WalletRequestSubscriberTests.swift @@ -57,7 +57,7 @@ class WalletRequestSubscriberTests: XCTestCase { pairingRegisterer.subject.send(payload) wait(for: [messageExpectation], timeout: defaultTimeout) - XCTAssertTrue(pairingRegisterer.isActivateCalled) + XCTAssertTrue(pairingRegisterer.isReceivedCalled) XCTAssertEqual(requestPayload, expectedPayload) XCTAssertEqual(requestId, expectedRequestId) } diff --git a/Tests/TestingUtils/Mocks/PairingRegistererMock.swift b/Tests/TestingUtils/Mocks/PairingRegistererMock.swift index 6aa18183f..3c21567c6 100644 --- a/Tests/TestingUtils/Mocks/PairingRegistererMock.swift +++ b/Tests/TestingUtils/Mocks/PairingRegistererMock.swift @@ -7,6 +7,7 @@ public class PairingRegistererMock: PairingRegisterer where Reque public let subject = PassthroughSubject, Never>() public var isActivateCalled: Bool = false + public var isReceivedCalled: Bool = false public func register(method: ProtocolMethod) -> AnyPublisher, Never> where RequestParams: Decodable, RequestParams: Encodable { subject.eraseToAnyPublisher() as! AnyPublisher, Never> @@ -21,6 +22,6 @@ public class PairingRegistererMock: PairingRegisterer where Reque } public func setReceived(pairingTopic: String) { - + isReceivedCalled = true } } diff --git a/Tests/TestingUtils/NetworkingInteractorMock.swift b/Tests/TestingUtils/NetworkingInteractorMock.swift index 175e6aed0..8764e7675 100644 --- a/Tests/TestingUtils/NetworkingInteractorMock.swift +++ b/Tests/TestingUtils/NetworkingInteractorMock.swift @@ -15,6 +15,7 @@ public class NetworkingInteractorMock: NetworkInteracting { private(set) var didRespondError = false private(set) var didCallSubscribe = false private(set) var didCallUnsubscribe = false + private(set) var didCallHandleHistoryRequest = false private(set) var didRespondOnTopic: String? private(set) var lastErrorCode = -1 @@ -90,6 +91,10 @@ public class NetworkingInteractorMock: NetworkInteracting { subscriptions.append(topic) didCallSubscribe = true } + + public func handleHistoryRequest(topic: String, request: JSONRPC.RPCRequest) { + didCallHandleHistoryRequest = true + } func didSubscribe(to topic: String) -> Bool { subscriptions.contains { $0 == topic } diff --git a/Tests/WalletConnectPairingTests/WalletPairServiceTests.swift b/Tests/WalletConnectPairingTests/WalletPairServiceTests.swift index facccf6b4..4f88e9c94 100644 --- a/Tests/WalletConnectPairingTests/WalletPairServiceTests.swift +++ b/Tests/WalletConnectPairingTests/WalletPairServiceTests.swift @@ -11,12 +11,14 @@ final class WalletPairServiceTestsTests: XCTestCase { var networkingInteractor: NetworkingInteractorMock! var storageMock: WCPairingStorageMock! var cryptoMock: KeyManagementServiceMock! + var rpcHistory: RPCHistory! override func setUp() { networkingInteractor = NetworkingInteractorMock() storageMock = WCPairingStorageMock() cryptoMock = KeyManagementServiceMock() - service = WalletPairService(networkingInteractor: networkingInteractor, kms: cryptoMock, pairingStorage: storageMock) + rpcHistory = RPCHistoryFactory.createForNetwork(keyValueStorage: RuntimeKeyValueStorage()) + service = WalletPairService(networkingInteractor: networkingInteractor, kms: cryptoMock, pairingStorage: storageMock, history: rpcHistory) } func testPairWhenNetworkNotConnectedThrows() async { @@ -25,13 +27,18 @@ final class WalletPairServiceTestsTests: XCTestCase { await XCTAssertThrowsErrorAsync(try await service.pair(uri)) } - func testPairOnSameURIWhenRequestReceivedThrows() async { + func testPairOnSameUriPresentsRequest() async { + let rpcRequest = RPCRequest(method: "session_propose", id: 1234) + let uri = WalletConnectURI.stub() try! await service.pair(uri) var pairing = storageMock.getPairing(forTopic: uri.topic) pairing?.receivedRequest() storageMock.setPairing(pairing!) - await XCTAssertThrowsErrorAsync(try await service.pair(uri)) + try! rpcHistory.set(rpcRequest, forTopic: uri.topic, emmitedBy: .local) + + try! await service.pair(uri) + XCTAssertTrue(networkingInteractor.didCallHandleHistoryRequest) } func testPair() async {