Skip to content

Commit

Permalink
#14 expose json rpc history
Browse files Browse the repository at this point in the history
  • Loading branch information
llbartekll committed Jan 14, 2022
1 parent 4d81217 commit 46c965a
Show file tree
Hide file tree
Showing 10 changed files with 77 additions and 43 deletions.
6 changes: 3 additions & 3 deletions Example/ExampleApp/Responder/ResponderViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,11 @@ final class ResponderViewController: UIViewController {
let requestVC = RequestViewController(sessionRequest)
requestVC.onSign = { [unowned self] in
let result = signEth(request: sessionRequest)
let response = JSONRPCResponse<AnyCodable>(id: sessionRequest.id, result: result)
let response = JSONRPCResponse<AnyCodable>(id: sessionRequest.id!, result: result)
client.respond(topic: sessionRequest.topic, response: .response(response))
}
requestVC.onReject = { [weak self] in
self?.client.respond(topic: sessionRequest.topic, response: .error(JSONRPCErrorResponse(id: sessionRequest.id, error: JSONRPCErrorResponse.Error(code: 0, message: ""))))
self?.client.respond(topic: sessionRequest.topic, response: .error(JSONRPCErrorResponse(id: sessionRequest.id!, error: JSONRPCErrorResponse.Error(code: 0, message: ""))))
}
present(requestVC, animated: true)
}
Expand Down Expand Up @@ -167,7 +167,7 @@ extension ResponderViewController: WalletConnectClientDelegate {
dappURL: appMetadata.url ?? "",
iconURL: appMetadata.icons?.first ?? "",
chains: Array(sessionProposal.permissions.blockchains),
methods: Array(sessionProposal.permissions.methods))
methods: Array(sessionProposal.permissions.methods), pendingRequests: [])
currentProposal = sessionProposal
DispatchQueue.main.async { // FIXME: Delegate being called from background thread
self.showSessionProposal(info)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ struct SessionInfo {
let iconURL: String
let chains: [String]
let methods: [String]
let pendingRequests: [String]
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@ final class SessionDetailsViewController: UIViewController, UITableViewDelegate,
private let client: WalletConnectClient
private let session: Session
init(_ session: Session, _ client: WalletConnectClient) {
let pendingRequests = client.getPendingRequests().map{$0.method}
self.sessionInfo = SessionInfo(name: session.peer.name ?? "",
descriptionText: session.peer.description ?? "",
dappURL: session.peer.description ?? "",
iconURL: session.peer.icons?.first ?? "",
chains: Array(session.permissions.blockchains),
methods: Array(session.permissions.methods))
descriptionText: session.peer.description ?? "",
dappURL: session.peer.description ?? "",
iconURL: session.peer.icons?.first ?? "",
chains: Array(session.permissions.blockchains),
methods: Array(session.permissions.methods),
pendingRequests: pendingRequests)
self.client = client
self.session = session
super.init(nibName: nil, bundle: nil)
Expand Down Expand Up @@ -65,7 +67,7 @@ final class SessionDetailsViewController: UIViewController, UITableViewDelegate,
} else if section == 1 {
return sessionInfo.methods.count
} else {
return 0
return sessionInfo.pendingRequests.count
}
}

Expand All @@ -76,7 +78,7 @@ final class SessionDetailsViewController: UIViewController, UITableViewDelegate,
} else if indexPath.section == 1 {
cell.textLabel?.text = sessionInfo.methods[indexPath.row]
} else {
// cell.textLabel?.text = sessionInfo.pendingRequests[indexPath.row]
cell.textLabel?.text = sessionInfo.pendingRequests[indexPath.row]
}
return cell
}
Expand Down
12 changes: 11 additions & 1 deletion Sources/WalletConnect/Engine/SessionEngine.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ final class SessionEngine {
private var publishers = [AnyCancellable]()
private let logger: ConsoleLogging
private let topicInitializer: () -> String?
let sessionPayloadsJsonRpcHistory: WalletConnectUtils.JsonRpcHistory<AnyCodable>

init(relay: WalletConnectRelaying,
crypto: CryptoStorageProtocol,
Expand All @@ -30,7 +31,8 @@ final class SessionEngine {
isController: Bool,
metadata: AppMetadata,
logger: ConsoleLogging,
topicGenerator: @escaping () -> String? = String.generateTopic) {
topicGenerator: @escaping () -> String? = String.generateTopic,
sessionPayloadsJsonRpcHistory: WalletConnectUtils.JsonRpcHistory<AnyCodable>) {
self.relayer = relay
self.crypto = crypto
self.metadata = metadata
Expand All @@ -39,6 +41,7 @@ final class SessionEngine {
self.isController = isController
self.logger = logger
self.topicInitializer = topicGenerator
self.sessionPayloadsJsonRpcHistory = sessionPayloadsJsonRpcHistory
setUpWCRequestHandling()
setupExpirationHandling()
restoreSubscriptions()
Expand Down Expand Up @@ -179,6 +182,11 @@ final class SessionEngine {
let request = SessionType.PayloadParams.Request(method: params.method, params: params.params)
let sessionPayloadParams = SessionType.PayloadParams(request: request, chainId: params.chainId)
let sessionPayloadRequest = WCRequest(method: .sessionPayload, params: .sessionPayload(sessionPayloadParams))


let recordRequest = JSONRPCRequest(id: sessionPayloadRequest.id, method: params.method, params: params.params)
try? sessionPayloadsJsonRpcHistory.set(topic: params.topic, request: recordRequest, chainId: params.chainId)

relayer.request(topic: params.topic, payload: sessionPayloadRequest) { [weak self] result in
switch result {
case .success(let response):
Expand Down Expand Up @@ -419,6 +427,8 @@ final class SessionEngine {
method: jsonRpcRequest.method,
params: jsonRpcRequest.params,
chainId: payloadParams.chainId)

try? sessionPayloadsJsonRpcHistory.set(topic: topic, request: jsonRpcRequest, chainId: payloadParams.chainId)
do {
try validatePayload(request)
onSessionPayloadRequest?(request)
Expand Down
4 changes: 2 additions & 2 deletions Sources/WalletConnect/JsonRpcHistory/JsonRpcHistory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@ protocol JsonRpcHistoryRecording {
func resolve(response: JsonRpcResponseTypes) throws -> JsonRpcRecord
func exist(id: Int64) -> Bool
}

//TODO -remove and use jsonrpc history only from utils
class JsonRpcHistory: JsonRpcHistoryRecording {
let storage: KeyValueStore<JsonRpcRecord>
let logger: ConsoleLogging
let identifier: String

init(logger: ConsoleLogging, keyValueStorage: KeyValueStorage, uniqueIdentifier: String? = nil) {
self.logger = logger
self.storage = KeyValueStore<JsonRpcRecord>(defaults: keyValueStorage)
self.storage = KeyValueStore<JsonRpcRecord>(defaults: keyValueStorage, identifier: "")
self.identifier = "com.walletconnect.sdk.\(uniqueIdentifier ?? "")"
}

Expand Down
2 changes: 1 addition & 1 deletion Sources/WalletConnect/Request.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import Foundation
import WalletConnectUtils

public struct Request: Codable, Equatable {
public let id: Int64
public internal(set) var id: Int64?
public let topic: String
public let method: String
public let params: AnyCodable
Expand Down
22 changes: 19 additions & 3 deletions Sources/WalletConnect/WalletConnectClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@ import WalletConnectUtils
import UIKit
#endif



enum StorageDomainIdentifiers {
static func sessionPayloadsJsonRpcHistory(clientName: String) -> String {
return "com.walletconnect.sdk.\(clientName).jsonRpcHistory.sessionPayloads"
}
}

/// An Object that expose public API to provide interactions with WalletConnect SDK
///
/// WalletConnect Client is not a singleton but once you create an instance, you should not deinitialise it. Usually only one instance of a client is required in the application.
Expand All @@ -30,7 +38,7 @@ public final class WalletConnectClient {
private let crypto: Crypto
private let secureStorage: SecureStorage
private let pairingQueue = DispatchQueue(label: "com.walletconnect.sdk.client.pairing", qos: .userInitiated)

// MARK: - Initializers

/// Initializes and returns newly created WalletConnect Client Instance. Establishes a network connection with the relay
Expand Down Expand Up @@ -62,7 +70,9 @@ public final class WalletConnectClient {
let pairingSequencesStore = PairingStorage(storage: SequenceStore<PairingSequence>(storage: keyValueStore, uniqueIdentifier: clientName))
let sessionSequencesStore = SessionStorage(storage: SequenceStore<SessionSequence>(storage: keyValueStore, uniqueIdentifier: clientName))
self.pairingEngine = PairingEngine(relay: relay, crypto: crypto, subscriber: WCSubscriber(relay: relay, logger: logger), sequencesStore: pairingSequencesStore, isController: isController, metadata: metadata, logger: logger)
self.sessionEngine = SessionEngine(relay: relay, crypto: crypto, subscriber: WCSubscriber(relay: relay, logger: logger), sequencesStore: sessionSequencesStore, isController: isController, metadata: metadata, logger: logger)
let sessionPayloadsJsonRpcHistoryIdentifier = StorageDomainIdentifiers.sessionPayloadsJsonRpcHistory(clientName: clientName ?? "")
let sessionPayloadsJsonRpcHistory = WalletConnectUtils.JsonRpcHistory<AnyCodable>(logger: logger, keyValueStorage: keyValueStore, identifier: sessionPayloadsJsonRpcHistoryIdentifier)
self.sessionEngine = SessionEngine(relay: relay, crypto: crypto, subscriber: WCSubscriber(relay: relay, logger: logger), sequencesStore: sessionSequencesStore, isController: isController, metadata: metadata, logger: logger, sessionPayloadsJsonRpcHistory: sessionPayloadsJsonRpcHistory)
setUpEnginesCallbacks()
subscribeNotificationCenter()
}
Expand Down Expand Up @@ -221,7 +231,13 @@ public final class WalletConnectClient {
pairingEngine.getSettledPairings()
}

//MARK: - Private
public func getPendingRequests() -> [Request] {
sessionEngine.sessionPayloadsJsonRpcHistory.getPending().compactMap {
return Request(id: $0.id, topic: $0.topic, method: $0.request.method, params: $0.request.params, chainId: $0.chainId)
}
}

// MARK: - Private

private func setUpEnginesCallbacks() {
pairingEngine.onSessionProposal = { [unowned self] proposal in
Expand Down
26 changes: 12 additions & 14 deletions Sources/WalletConnectUtils/JsonRpcHistory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,50 +7,48 @@ public class JsonRpcHistory<T> where T: Codable&Equatable {
}
private let storage: KeyValueStore<JsonRpcRecord>
private let logger: ConsoleLogging
private let identifier: String


public init(logger: ConsoleLogging, keyValueStorage: KeyValueStorage, identifier: String) {
self.logger = logger
self.storage = KeyValueStore<JsonRpcRecord>(defaults: keyValueStorage)
self.identifier = identifier
self.storage = KeyValueStore<JsonRpcRecord>(defaults: keyValueStorage, identifier: identifier)
}

public func get(id: Int64) -> JsonRpcRecord? {
try? storage.get(key: getKey(for: id))
try? storage.get(key: "\(id)")
}

public func set(topic: String, request: JSONRPCRequest<T>) throws {
public func set(topic: String, request: JSONRPCRequest<T>, chainId: String? = nil) throws {
guard !exist(id: request.id) else {
throw RecordingError.jsonRpcDuplicateDetected
}
logger.debug("Setting JSON-RPC request history record")
let record = JsonRpcRecord(id: request.id, topic: topic, request: JsonRpcRecord.Request(method: request.method, params: AnyCodable(request.params)), response: nil)
try storage.set(record, forKey: getKey(for: request.id))
let record = JsonRpcRecord(id: request.id, topic: topic, request: JsonRpcRecord.Request(method: request.method, params: AnyCodable(request.params)), response: nil, chainId: chainId)
try storage.set(record, forKey: "\(request.id)")
}

public func delete(topic: String) {
storage.getAll().forEach { record in
if record.topic == topic {
storage.delete(forKey: getKey(for: record.id))
storage.delete(forKey: "\(record.id)")
}
}
}

public func resolve(response: JsonRpcResponseTypes) throws {
guard var record = try? storage.get(key: getKey(for: response.id)) else { return }
guard var record = try? storage.get(key: "\(response.id)") else { return }
if record.response != nil {
throw RecordingError.jsonRpcDuplicateDetected
} else {
record.response = response
try storage.set(record, forKey: getKey(for: record.id))
try storage.set(record, forKey: "\(record.id)")
}
}

public func exist(id: Int64) -> Bool {
return (try? storage.get(key: getKey(for: id))) != nil
return (try? storage.get(key: "\(id)")) != nil
}

private func getKey(for id: Int64) -> String {
return "com.walletconnect.sdk.\(identifier).\(id)"
public func getPending() -> [JsonRpcRecord] {
storage.getAll().filter{$0.response == nil}
}
}
16 changes: 8 additions & 8 deletions Sources/WalletConnectUtils/JsonRpcRecord.swift
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@

import Foundation
import WalletConnectUtils

public struct JsonRpcRecord: Codable {
let id: Int64
let topic: String
let request: Request
var response: JsonRpcResponseTypes?
public let id: Int64
public let topic: String
public let request: Request
public var response: JsonRpcResponseTypes?
public let chainId: String?

struct Request: Codable {
let method: String
let params: AnyCodable
public struct Request: Codable {
public let method: String
public let params: AnyCodable
}
}

15 changes: 11 additions & 4 deletions Sources/WalletConnectUtils/KeyValueStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,27 @@ import Foundation

public final class KeyValueStore<T> where T: Codable {
private let defaults: KeyValueStorage
private let prefix: String

public init(defaults: KeyValueStorage) {
public init(defaults: KeyValueStorage, identifier: String) {
self.defaults = defaults
self.prefix = identifier
}

public func set(_ item: T, forKey key: String) throws {
let encoded = try JSONEncoder().encode(item)
defaults.set(encoded, forKey: key)
defaults.set(encoded, forKey: getContextPrefixKey(for: key))
}

public func get(key: String) throws -> T? {
guard let data = defaults.object(forKey: key) as? Data else { return nil }
guard let data = defaults.object(forKey: getContextPrefixKey(for: key)) as? Data else { return nil }
let item = try JSONDecoder().decode(T.self, from: data)
return item
}

public func getAll() -> [T] {
return defaults.dictionaryRepresentation().compactMap {
guard $0.key.hasPrefix(prefix) else {return nil}
if let data = $0.value as? Data,
let item = try? JSONDecoder().decode(T.self, from: data) {
return item
Expand All @@ -30,6 +33,10 @@ public final class KeyValueStore<T> where T: Codable {
}

public func delete(forKey key: String) {
defaults.removeObject(forKey: key)
defaults.removeObject(forKey: getContextPrefixKey(for: key))
}

private func getContextPrefixKey(for key: String) -> String {
return "\(prefix).\(key)"
}
}

0 comments on commit 46c965a

Please sign in to comment.