Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Requests queue #1295

Merged
merged 10 commits into from
Jan 31, 2024
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -73,14 +73,23 @@
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "C56EE21A293F55ED004840D1"
BuildableName = "WalletApp.app"
BlueprintName = "WalletApp"
ReferencedContainer = "container:ExampleApp.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "764E1D3B26F8D3FC00A1FB15"
BuildableName = "WalletConnect Wallet.app"
BlueprintName = "Wallet"
ReferencedContainer = "container:ExampleApp.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</MacroExpansion>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
Expand All @@ -98,6 +107,15 @@
ReferencedContainer = "container:ExampleApp.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "764E1D3B26F8D3FC00A1FB15"
BuildableName = "WalletConnect Wallet.app"
BlueprintName = "Wallet"
ReferencedContainer = "container:ExampleApp.xcodeproj">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import UIKit
import Combine
import SwiftUI

final class MainPresenter {
private let interactor: MainInteractor
Expand Down Expand Up @@ -48,10 +49,15 @@ extension MainPresenter {

interactor.sessionRequestPublisher
.receive(on: DispatchQueue.main)
.sink { [unowned self] request, context in
.sink { [unowned self] (request, context) in
guard let vc = UIApplication.currentWindow.rootViewController?.topController,
vc.restorationIdentifier != SessionRequestModule.restorationIdentifier else {
return
}
router.dismiss()
router.present(sessionRequest: request, importAccount: importAccount, sessionContext: context)
}.store(in: &disposeBag)


interactor.requestPublisher
.receive(on: DispatchQueue.main)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@ import SwiftUI
import Web3Wallet

final class SessionRequestModule {
static let restorationIdentifier = "SessionRequestViewController"
@discardableResult
static func create(app: Application, sessionRequest: Request, importAccount: ImportAccount, sessionContext: VerifyContext?) -> UIViewController {
let router = SessionRequestRouter(app: app)
let interactor = SessionRequestInteractor()
let presenter = SessionRequestPresenter(interactor: interactor, router: router, sessionRequest: sessionRequest, importAccount: importAccount, context: sessionContext)
let view = SessionRequestView().environmentObject(presenter)
let viewController = SceneViewController(viewModel: presenter, content: view)
viewController.restorationIdentifier = Self.restorationIdentifier

router.viewController = viewController

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import UIKit
import Combine
import WalletConnectNetworking
import Web3Wallet

final class SettingsPresenter: ObservableObject {

Expand Down Expand Up @@ -46,6 +47,7 @@ final class SettingsPresenter: ObservableObject {
guard let account = accountStorage.importAccount?.account else { return }
try await interactor.notifyUnregister(account: account)
accountStorage.importAccount = nil
try await Web3Wallet.instance.cleanup()
llbartekll marked this conversation as resolved.
Show resolved Hide resolved
UserDefaults.standard.set(nil, forKey: "deviceToken")
await router.presentWelcome()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ final class WalletRouter {

func present(sessionRequest: Request, importAccount: ImportAccount, sessionContext: VerifyContext?) {
SessionRequestModule.create(app: app, sessionRequest: sessionRequest, importAccount: importAccount, sessionContext: sessionContext)
.presentFullScreen(from: viewController, transparentBackground: true)
.presentFullScreen(from: UIApplication.currentWindow.rootViewController!, transparentBackground: true)
}

func present(sessionProposal: Session.Proposal, importAccount: ImportAccount, sessionContext: VerifyContext?) {
Expand Down
25 changes: 20 additions & 5 deletions Sources/WalletConnectSign/Engine/Common/SessionEngine.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,16 @@ final class SessionEngine {
}

var onSessionsUpdate: (([Session]) -> Void)?
var onSessionRequest: ((Request, VerifyContext?) -> Void)?
var onSessionResponse: ((Response) -> Void)?
var onSessionRejected: ((String, SessionType.Reason) -> Void)?
var onSessionDelete: ((String, SessionType.Reason) -> Void)?
var onEventReceived: ((String, Session.Event, Blockchain?) -> Void)?

var sessionRequestPublisher: AnyPublisher<(request: Request, context: VerifyContext?), Never> {
return sessionRequestsProvider.sessionRequestPublisher
}


private let sessionStore: WCSessionStorage
private let networkingInteractor: NetworkInteracting
private let historyService: HistoryService
Expand All @@ -21,6 +25,7 @@ final class SessionEngine {
private let kms: KeyManagementServiceProtocol
private var publishers = [AnyCancellable]()
private let logger: ConsoleLogging
private let sessionRequestsProvider: SessionRequestsProvider

init(
networkingInteractor: NetworkInteracting,
Expand All @@ -29,7 +34,8 @@ final class SessionEngine {
verifyClient: VerifyClientProtocol,
kms: KeyManagementServiceProtocol,
sessionStore: WCSessionStorage,
logger: ConsoleLogging
logger: ConsoleLogging,
sessionRequestsProvider: SessionRequestsProvider
) {
self.networkingInteractor = networkingInteractor
self.historyService = historyService
Expand All @@ -38,12 +44,17 @@ final class SessionEngine {
self.kms = kms
self.sessionStore = sessionStore
self.logger = logger
self.sessionRequestsProvider = sessionRequestsProvider

setupConnectionSubscriptions()
setupRequestSubscriptions()
setupResponseSubscriptions()
setupUpdateSubscriptions()
setupExpirationSubscriptions()
DispatchQueue.main.asyncAfter(deadline: .now() + 1) { [weak self] in
guard let self = self else {return}
sessionRequestsProvider.emitRequestIfPending()
}
}

func hasSession(for topic: String) -> Bool {
Expand Down Expand Up @@ -95,6 +106,10 @@ final class SessionEngine {
protocolMethod: protocolMethod
)
verifyContextStore.delete(forKey: requestId.string)
DispatchQueue.main.asyncAfter(deadline: .now() + 1) { [weak self] in
guard let self = self else {return}
sessionRequestsProvider.emitRequestIfPending()
}
}

func emit(topic: String, event: SessionType.EventParams.Event, chainId: Blockchain) async throws {
Expand Down Expand Up @@ -249,12 +264,12 @@ private extension SessionEngine {
let response = try await verifyClient.verifyOrigin(assertionId: assertionId)
let verifyContext = verifyClient.createVerifyContext(origin: response.origin, domain: session.peerParticipant.metadata.url, isScam: response.isScam)
verifyContextStore.set(verifyContext, forKey: request.id.string)
onSessionRequest?(request, verifyContext)

sessionRequestsProvider.emitRequestIfPending()
} catch {
let verifyContext = verifyClient.createVerifyContext(origin: nil, domain: session.peerParticipant.metadata.url, isScam: nil)
verifyContextStore.set(verifyContext, forKey: request.id.string)
onSessionRequest?(request, verifyContext)
return
sessionRequestsProvider.emitRequestIfPending()
}
}
}
Expand Down
29 changes: 21 additions & 8 deletions Sources/WalletConnectSign/Services/HistoryService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,32 +15,45 @@ final class HistoryService {

public func getSessionRequest(id: RPCID) -> (request: Request, context: VerifyContext?)? {
guard let record = history.get(recordId: id) else { return nil }
guard let (request, recordId) = mapRequestRecord(record) else {
guard let (request, recordId, _) = mapRequestRecord(record) else {
return nil
}
return (request, try? verifyContextStore.get(key: recordId.string))
}

func getPendingRequests() -> [(request: Request, context: VerifyContext?)] {
getPendingRequestsSortedByTimestamp()
}

func getPendingRequestsSortedByTimestamp() -> [(request: Request, context: VerifyContext?)] {
let requests = history.getPending()
.compactMap { mapRequestRecord($0) }
.filter { !$0.0.isExpired() } // Note the change here to access the Request part of the tuple
return requests.map { (request: $0.0, context: try? verifyContextStore.get(key: $0.1.string)) }
}
.filter { !$0.0.isExpired() }
.sorted {
switch ($0.2, $1.2) {
case let (date1?, date2?): return date1 < date2 // Both dates are present
case (nil, _): return false // First date is nil, so it should go last
case (_, nil): return true // Second date is nil, so the first one should come first
}
}
.map { (request: $0.0, context: try? verifyContextStore.get(key: $0.1.string)) }

return requests
}

func getPendingRequestsWithRecordId() -> [(request: Request, recordId: RPCID)] {
history.getPending()
return history.getPending()
.compactMap { mapRequestRecord($0) }
.map { (request: $0.0, recordId: $0.1) }
}

func getPendingRequests(topic: String) -> [(request: Request, context: VerifyContext?)] {
return getPendingRequests().filter { $0.request.topic == topic }
return getPendingRequestsSortedByTimestamp().filter { $0.request.topic == topic }
}
}

private extension HistoryService {
func mapRequestRecord(_ record: RPCHistory.Record) -> (Request, RPCID)? {
func mapRequestRecord(_ record: RPCHistory.Record) -> (Request, RPCID, Date?)? {
guard let request = try? record.request.params?.get(SessionType.RequestParams.self)
else { return nil }

Expand All @@ -53,6 +66,6 @@ private extension HistoryService {
expiryTimestamp: request.request.expiryTimestamp
)

return (mappedRequest, record.id)
return (mappedRequest, record.id, record.timestamp)
}
}
6 changes: 5 additions & 1 deletion Sources/WalletConnectSign/Services/SignCleanupService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,16 @@ final class SignCleanupService {
private let kms: KeyManagementServiceProtocol
private let sessionTopicToProposal: CodableStore<Session.Proposal>
private let networkInteractor: NetworkInteracting
private let rpcHistory: RPCHistory

init(pairingStore: WCPairingStorage, sessionStore: WCSessionStorage, kms: KeyManagementServiceProtocol, sessionTopicToProposal: CodableStore<Session.Proposal>, networkInteractor: NetworkInteracting) {
init(pairingStore: WCPairingStorage, sessionStore: WCSessionStorage, kms: KeyManagementServiceProtocol, sessionTopicToProposal: CodableStore<Session.Proposal>, networkInteractor: NetworkInteracting,
rpcHistory: RPCHistory) {
self.pairingStore = pairingStore
self.sessionStore = sessionStore
self.sessionTopicToProposal = sessionTopicToProposal
self.networkInteractor = networkInteractor
self.kms = kms
self.rpcHistory = rpcHistory
}

func cleanup() async throws {
Expand All @@ -39,6 +42,7 @@ private extension SignCleanupService {
pairingStore.deleteAll()
sessionStore.deleteAll()
sessionTopicToProposal.deleteAll()
rpcHistory.deleteAll()
try kms.deleteAll()
}
}
44 changes: 44 additions & 0 deletions Sources/WalletConnectSign/Sign/SessionRequestsProvider.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import Combine
import Foundation

class SessionRequestsProvider {
private let historyService: HistoryService
private var sessionRequestPublisherSubject = PassthroughSubject<(request: Request, context: VerifyContext?), Never>()
private var cancellables = Set<AnyCancellable>()
private var lastEmitDate: Date?
private var emitRequestSubject = PassthroughSubject<Void, Never>()

public var sessionRequestPublisher: AnyPublisher<(request: Request, context: VerifyContext?), Never> {
sessionRequestPublisherSubject.eraseToAnyPublisher()
}

init(historyService: HistoryService) {
self.historyService = historyService
setupEmitRequestHandling()
}

private func setupEmitRequestHandling() {
emitRequestSubject
.sink { [weak self] _ in
guard let self = self else { return }
let now = Date()
if let lastEmitDate = self.lastEmitDate, now.timeIntervalSince(lastEmitDate) < 1 {
// If the last emit was less than 1 second ago, ignore this request.
return
}

// Update the last emit time to now.
self.lastEmitDate = now

// Fetch the oldest request and emit it.
if let oldestRequest = self.historyService.getPendingRequestsSortedByTimestamp().first {
self.sessionRequestPublisherSubject.send(oldestRequest)
}
}
.store(in: &cancellables)
}

func emitRequestIfPending() {
emitRequestSubject.send(())
flypaper0 marked this conversation as resolved.
Show resolved Hide resolved
}
}
6 changes: 1 addition & 5 deletions Sources/WalletConnectSign/Sign/SignClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public final class SignClient: SignClientProtocol {
///
/// In most cases event will be emited on wallet
public var sessionRequestPublisher: AnyPublisher<(request: Request, context: VerifyContext?), Never> {
sessionRequestPublisherSubject.eraseToAnyPublisher()
sessionEngine.sessionRequestPublisher
}

/// Publisher that sends web socket connection status
Expand Down Expand Up @@ -138,7 +138,6 @@ public final class SignClient: SignClientProtocol {
private let requestsExpiryWatcher: RequestsExpiryWatcher

private let sessionProposalPublisherSubject = PassthroughSubject<(proposal: Session.Proposal, context: VerifyContext?), Never>()
private let sessionRequestPublisherSubject = PassthroughSubject<(request: Request, context: VerifyContext?), Never>()
private let socketConnectionStatusPublisherSubject = PassthroughSubject<SocketConnectionStatus, Never>()
private let sessionSettlePublisherSubject = PassthroughSubject<Session, Never>()
private let sessionDeletePublisherSubject = PassthroughSubject<(String, Reason), Never>()
Expand Down Expand Up @@ -359,9 +358,6 @@ public final class SignClient: SignClientProtocol {
approveEngine.onSessionSettle = { [unowned self] settledSession in
sessionSettlePublisherSubject.send(settledSession)
}
sessionEngine.onSessionRequest = { [unowned self] (sessionRequest, context) in
sessionRequestPublisherSubject.send((sessionRequest, context))
}
sessionEngine.onSessionDelete = { [unowned self] topic, reason in
sessionDeletePublisherSubject.send((topic, reason))
}
Expand Down
5 changes: 3 additions & 2 deletions Sources/WalletConnectSign/Sign/SignClientFactory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ public struct SignClientFactory {
let verifyContextStore = CodableStore<VerifyContext>(defaults: keyValueStorage, identifier: VerifyStorageIdentifiers.context.rawValue)
let historyService = HistoryService(history: rpcHistory, verifyContextStore: verifyContextStore)
let verifyClient = VerifyClientFactory.create()
let sessionEngine = SessionEngine(networkingInteractor: networkingClient, historyService: historyService, verifyContextStore: verifyContextStore, verifyClient: verifyClient, kms: kms, sessionStore: sessionStore, logger: logger)
let sessionRequestsProvider = SessionRequestsProvider(historyService: historyService)
let sessionEngine = SessionEngine(networkingInteractor: networkingClient, historyService: historyService, verifyContextStore: verifyContextStore, verifyClient: verifyClient, kms: kms, sessionStore: sessionStore, logger: logger, sessionRequestsProvider: sessionRequestsProvider)
let nonControllerSessionStateMachine = NonControllerSessionStateMachine(networkingInteractor: networkingClient, kms: kms, sessionStore: sessionStore, logger: logger)
let controllerSessionStateMachine = ControllerSessionStateMachine(networkingInteractor: networkingClient, kms: kms, sessionStore: sessionStore, logger: logger)
let sessionExtendRequester = SessionExtendRequester(sessionStore: sessionStore, networkingInteractor: networkingClient)
Expand All @@ -64,7 +65,7 @@ public struct SignClientFactory {
verifyClient: verifyClient,
rpcHistory: rpcHistory
)
let cleanupService = SignCleanupService(pairingStore: pairingStore, sessionStore: sessionStore, kms: kms, sessionTopicToProposal: sessionTopicToProposal, networkInteractor: networkingClient)
let cleanupService = SignCleanupService(pairingStore: pairingStore, sessionStore: sessionStore, kms: kms, sessionTopicToProposal: sessionTopicToProposal, networkInteractor: networkingClient, rpcHistory: rpcHistory)
let deleteSessionService = DeleteSessionService(networkingInteractor: networkingClient, kms: kms, sessionStore: sessionStore, logger: logger)
let disconnectService = DisconnectService(deleteSessionService: deleteSessionService, sessionStorage: sessionStore)
let sessionPingService = SessionPingService(sessionStorage: sessionStore, networkingInteractor: networkingClient, logger: logger)
Expand Down
6 changes: 5 additions & 1 deletion Sources/WalletConnectUtils/RPCHistory/RPCHistory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public final class RPCHistory {
case .responseDuplicateNotAllowed:
return "Response duplicates are not allowed."
case .requestMatchingResponseNotFound:
return "Matching requesr for the response not found."
return "Matching request for the response not found."
}
}
}
Expand Down Expand Up @@ -119,6 +119,10 @@ public final class RPCHistory {
public func getPending() -> [Record] {
storage.getAll().filter { $0.response == nil }
}

public func deleteAll() {
storage.deleteAll()
}
}

extension RPCHistory {
Expand Down
Loading
Loading