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

[W3M] Showcase Chat integration #902

Merged
merged 2 commits into from
Jun 12, 2023
Merged
Show file tree
Hide file tree
Changes from all 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 @@ -15,8 +15,8 @@
"repositoryURL": "https://github.com/krzyzanowskim/CryptoSwift.git",
"state": {
"branch": null,
"revision": "eee9ad754926c40a0f7e73f152357d37b119b7fa",
"version": "1.7.1"
"revision": "19b3c3ceed117c5cc883517c4e658548315ba70b",
"version": "1.6.0"
}
},
{
Expand All @@ -33,8 +33,8 @@
"repositoryURL": "https://github.com/mxcl/PromiseKit.git",
"state": {
"branch": null,
"revision": "8a98e31a47854d3180882c8068cc4d9381bf382d",
"version": "6.22.1"
"revision": "43772616c46a44a9977e41924ae01d0e55f2f9ca",
"version": "6.18.1"
}
},
{
Expand Down Expand Up @@ -105,8 +105,8 @@
"repositoryURL": "https://github.com/bigearsenal/task-retrying-swift.git",
"state": {
"branch": null,
"revision": "1249b3524378423c848cef39fb220041e00a08ec",
"version": "1.0.4"
"revision": "645eaaf207a6f39ab4b469558d916ae23df199b5",
"version": "1.0.3"
}
},
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,3 @@ final class AccountStorage {
}
}
}

private extension ImportAccount {

var storageId: String {
switch self {
case .swift, .kotlin, .js:
return name
case .custom(let privateKey):
return privateKey
}
}
}
75 changes: 68 additions & 7 deletions Example/Showcase/Classes/DomainLayer/Chat/ChatService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import Foundation
import Combine
import WalletConnectChat
import WalletConnectRelay
import WalletConnectSign

typealias Stream<T> = AnyPublisher<T, Never>

Expand Down Expand Up @@ -81,7 +82,7 @@ final class ChatService {
try await client.reject(inviteId: invite.id)
}

func goPublic(account: Account, privateKey: String) async throws {
func goPublic(account: Account) async throws {
try await client.goPublic(account: account)
}

Expand All @@ -91,17 +92,15 @@ final class ChatService {
try await client.invite(invite: invite)
}

func register(account: Account, privateKey: String) async throws {
func register(account: Account, importAccount: ImportAccount) async throws {
_ = try await client.register(account: account) { message in
let signature = self.onSign(message: message, privateKey: privateKey)
return SigningResult.signed(signature)
return await self.onSign(message: message, importAccount: importAccount)
}
}

func unregister(account: Account, privateKey: String) async throws {
func unregister(account: Account, importAccount: ImportAccount) async throws {
try await client.unregister(account: account) { message in
let signature = self.onSign(message: message, privateKey: privateKey)
return SigningResult.signed(signature)
return await self.onSign(message: message, importAccount: importAccount)
}
}

Expand All @@ -116,9 +115,71 @@ final class ChatService {

private extension ChatService {

func onSign(message: String, importAccount: ImportAccount) async -> SigningResult {
switch importAccount {
case .swift, .kotlin, .js, .custom:
return .signed(onSign(message: message, privateKey: importAccount.privateKey))
case .web3Modal(let account, let topic):
return await onWeb3ModalSign(message: message, account: account, topic: topic)
}
}

func onSign(message: String, privateKey: String) -> CacaoSignature {
let privateKey = Data(hex: privateKey)
let signer = MessageSignerFactory(signerFactory: DefaultSignerFactory()).create()
return try! signer.sign(message: message, privateKey: privateKey, type: .eip191)
}

func onWeb3ModalSign(message: String, account: Account, topic: String) async -> SigningResult {
guard let session = Sign.instance.getSessions().first(where: { $0.topic == topic }) else { return .rejected }

do {
let request = makeRequest(session: session, message: message, account: account)
try await Sign.instance.request(params: request)

let signature: CacaoSignature = try await withCheckedThrowingContinuation { continuation in
var cancellable: AnyCancellable?
cancellable = Sign.instance.sessionResponsePublisher
.sink { response in
defer { cancellable?.cancel() }
switch response.result {
case .response(let value):
do {
let string = try value.get(String.self)
let signature = CacaoSignature(t: .eip191, s: string.deleting0x())
continuation.resume(returning: signature)
} catch {
continuation.resume(throwing: error)
}
case .error(let error):
continuation.resume(throwing: error)
}
}
}

return .signed(signature)
} catch {
return .rejected
}
}

func makeRequest(session: WalletConnectSign.Session, message: String, account: Account) -> Request {
return Request(
topic: session.topic,
method: "personal_sign",
params: AnyCodable(["0x" + message.data(using: .utf8)!.toHexString(), account.address]),
chainId: Blockchain("eip155:1")!
)
}
}

fileprivate extension String {

func deleting0x() -> String {
var string = self
if starts(with: "0x") {
string.removeFirst(2)
}
return string
}
}
55 changes: 40 additions & 15 deletions Example/Showcase/Classes/DomainLayer/Chat/ImportAccount.swift
Original file line number Diff line number Diff line change
@@ -1,41 +1,62 @@
import Foundation
import Web3
import WalletConnectSign

enum ImportAccount {
enum ImportAccount: Codable {
case swift
case kotlin
case js
case custom(privateKey: String)
case web3Modal(account: Account, topic: String)

static let swiftId = "swift.eth"
static let kotlinId = "kotlin.eth"
static let jsId = "js.eth"
static let privateKeyId = "privateKey"
static let web3ModalId = "web3Modal"

init?(input: String) {
switch input.lowercased() {
case ImportAccount.swift.name:
case ImportAccount.swiftId:
self = .swift
case ImportAccount.kotlin.name:
case ImportAccount.kotlinId:
self = .kotlin
case ImportAccount.js.name:
case ImportAccount.jsId:
self = .js
default:
if let _ = try? EthereumPrivateKey(hexPrivateKey: "0x" + input, ctx: nil) {
self = .custom(privateKey: input)
} else if let _ = try? EthereumPrivateKey(hexPrivateKey: input, ctx: nil) {
self = .custom(privateKey: input.replacingOccurrences(of: "0x", with: ""))
} else {
switch true {
case input.starts(with: ImportAccount.privateKeyId):
if let _ = try? EthereumPrivateKey(hexPrivateKey: "0x" + input, ctx: nil) {
self = .custom(privateKey: input)
} else if let _ = try? EthereumPrivateKey(hexPrivateKey: input, ctx: nil) {
self = .custom(privateKey: input.replacingOccurrences(of: "0x", with: ""))
} else {
return nil
}
case input.starts(with: ImportAccount.web3ModalId):
let components = input.components(separatedBy: "-")
guard components.count == 3, let account = Account(components[1]) else {
return nil
}
self = .web3Modal(account: account, topic: components[2])
default:
return nil
}
}
}

var name: String {
var storageId: String {
switch self {
case .swift:
return "swift.eth"
return ImportAccount.swiftId
case .kotlin:
return "kotlin.eth"
return ImportAccount.kotlinId
case .js:
return "js.eth"
case .custom:
return account.address
return ImportAccount.jsId
case .custom(let privateKey):
return "\(ImportAccount.privateKeyId)-\(privateKey)"
case .web3Modal(let account, let topic):
return "\(ImportAccount.web3ModalId)-\(account.absoluteString)-\(topic)"
}
}

Expand All @@ -50,6 +71,8 @@ enum ImportAccount {
case .custom(let privateKey):
let address = try! EthereumPrivateKey(hexPrivateKey: "0x" + privateKey, ctx: nil).address.hex(eip55: true)
return Account("eip155:1:\(address)")!
case .web3Modal(let account, _):
return account
}
}

Expand All @@ -63,6 +86,8 @@ enum ImportAccount {
return "de15cb11963e9bde0a5cce06a5ee2bda1cf3a67be6fbcd7a4fc8c0e4c4db0298"
case .custom(let privateKey):
return privateKey
case .web3Modal:
fatalError("Private key not available")
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ final class ChatListInteractor {
func logout() async throws {
guard let importAccount = accountStorage.importAccount else { return }
try await chatService.goPrivate(account: importAccount.account)
try await chatService.unregister(account: importAccount.account, privateKey: importAccount.privateKey)
try await chatService.unregister(account: importAccount.account, importAccount: importAccount)
accountStorage.importAccount = nil
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,6 @@ final class ImportInteractor {
}

func register(importAccount: ImportAccount) async throws {
try await chatService.register(account: importAccount.account, privateKey: importAccount.privateKey)
try await chatService.register(account: importAccount.account, importAccount: importAccount)
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import UIKit
import Combine
import WalletConnectSign

final class ImportPresenter: ObservableObject {

Expand All @@ -18,6 +19,20 @@ final class ImportPresenter: ObservableObject {
@MainActor
func didPressWeb3Modal() async throws {
router.presentWeb3Modal()

let session: Session = try await withCheckedThrowingContinuation { continuation in
var cancellable: AnyCancellable?
cancellable = Sign.instance.sessionSettlePublisher.sink { session in
defer { cancellable?.cancel() }
return continuation.resume(returning: session)
}
}

guard let account = session.accounts.first(where: { $0.blockchain.absoluteString == "eip155:1" }) else {
throw AlertError(message: "Тo matching accounts found in namespaces")
}

try await importAccount(.web3Modal(account: account, topic: session.topic))
}

@MainActor
Expand Down Expand Up @@ -57,8 +72,8 @@ private extension ImportPresenter {

@MainActor
func importAccount(_ importAccount: ImportAccount) async throws {
try! await interactor.register(importAccount: importAccount)
interactor.save(importAccount: importAccount)
try await interactor.register(importAccount: importAccount)
router.presentChat(importAccount: importAccount)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ final class WelcomeInteractor {

func goPublic() async throws {
guard let importAccount = importAccount else { return }
try await chatService.goPublic(account: importAccount.account, privateKey: importAccount.privateKey)
try await chatService.goPublic(account: importAccount.account)
}
}

Expand Down
6 changes: 6 additions & 0 deletions Sources/WalletConnectSign/Session.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,10 @@ extension Session {
SessionType.EventParams.Event(name: name, data: data)
}
}

public var accounts: [Account] {
return namespaces.values.reduce(into: []) { result, namespace in
result = result + Array(namespace.accounts)
}
}
}