WalletConnect / WalletConnectSwift

WalletConnect Swift SDK
MIT License
309 stars 164 forks source link

Rainbow opens, but connection is not made #147

Open itsallmememe opened 1 year ago

itsallmememe commented 1 year ago

I'm trying to integrate the WalletConnect v2 Swift SDK into out iOS App.

The code below does not create any runtime errors that I can see. A uri is created and Rainbow does open, but there's no connection attempt.

Can you help me debug please?

I set up WalletConnect as follows

        Networking.configure(projectId: Self.ProjectId, socketFactory: WebSocketWrapper(url: url))

        let metadata = AppMetadata(name: "Source",
                                   description: "A mobile application acting as a Dapp to connect to a crypto wallet using the WalletConnect SDK",
                                   url: Self.ClientUrl.absoluteString,
                                   icons: ["https://i.ebayimg.com/images/g/5qgAAOSwoBtW3zvq/s-l400.jpg"])

        Pair.configure(metadata: metadata)
        Auth.configure(crypto: Web3Signer())

And I then initiate the contact with this. The RequestParams code is taken from your demo project and needs amended slightly.

private func connect(wallet: WalletConnectType) {
        Task {
            let uri = try await Pair.instance.create()
            try await Auth.instance.request(.stub(), topic: uri.topic)

            var urlComponents = URLComponents()
            urlComponents.scheme = "https"
            urlComponents.host = "rnbwapp.com"
            urlComponents.path = "/wc"
            urlComponents.queryItems = [URLQueryItem(name: "uri", value: uri.deeplinkUri)]

            let url = urlComponents.url

            await UIApplication.shared.open(url!)
        }
    }

private extension RequestParams {
    static func stub(
        domain: String = "service.invalid",
        chainId: String = "eip155:1",
        nonce: String = "32891756",
        aud: String = "https://service.invalid/login",
        nbf: String? = nil,
        exp: String? = nil,
        statement: String? = "I accept the ServiceOrg Terms of Service: https://service.invalid/tos",
        requestId: String? = nil,
        resources: [String]? = ["ipfs://bafybeiemxf5abjwjbikoz4mc3a3dla6ual3jsgpdr4cjr3oz3evfyavhwq/", "https://example.com/my-web2-claim.json"]
    ) -> RequestParams {
        return RequestParams(
            domain: domain,
            chainId: chainId,
            nonce: nonce,
            aud: aud,
            nbf: nbf,
            exp: exp,
            statement: statement,
            requestId: requestId,
            resources: resources
        )
    }
}

Here's the Web3Signer

public struct Web3Signer: EthereumSigner, CryptoProvider {
    public func sign(message: Data, with key: Data) throws -> EthereumSignature {
        let privateKey = try EthereumPrivateKey(privateKey: [UInt8](key))
        let signature = try privateKey.sign(message: message.bytes)
        return EthereumSignature(v: UInt8(signature.v), r: signature.r, s: signature.s)
    }

    public func recoverPubKey(signature: EthereumSignature, message: Data) throws -> Data {
        let publicKey = try EthereumPublicKey(
            message: message.bytes,
            v: EthereumQuantity(quantity: BigUInt(signature.v)),
            r: EthereumQuantity(signature.r),
            s: EthereumQuantity(signature.s)
        )
        return Data(publicKey.rawPublicKey)
    }

    public func keccak256(_ data: Data) -> Data {
        let digest = SHA3(variant: .keccak256)
        let hash = digest.calculate(for: [UInt8](data))
        return Data(hash)
    }
}

And the SocketFactory

struct SocketFactory: WebSocketFactory {
    func create(with url: URL) -> WebSocketConnecting {
        return WebSocketWrapper(url: url)
    }
}

enum WebSocketWrapperError: Error {
    case disconnected(String, UInt16)
}

class WebSocketWrapper {
    private let websocket: WebSocket

    var request: URLRequest {
        didSet {
            websocket.request = request
        }
    }

    var isConnected: Bool = false
    var onConnect: (() -> Void)?
    var onDisconnect: ((Error?) -> Void)?

    var onText: ((String) -> Void)?

    init(url: URL) {
        request = URLRequest(url: url)
        request.addValue("relay.walletconnect.com", forHTTPHeaderField: "Origin")

        websocket = WebSocket(request: request)
        websocket.delegate = self
    }

}

extension WebSocketWrapper: WebSocketDelegate {
    func didReceive(event: Starscream.WebSocketEvent, client: Starscream.WebSocket) {
        switch event {
        case .connected(_):
            isConnected = true
            onConnect?()

        case let .disconnected(string, code):
            isConnected = false
            onDisconnect?(WebSocketWrapperError.disconnected(string, code))

        case .text(let text):
            onText?(text)

        case .binary(_): break
        case .cancelled: break
        case .error(_): break
        case .ping(_): break
        case .pong(_): break
        case .reconnectSuggested(_): break
        case .viabilityChanged(_): break
        }
    }
}

extension WebSocketWrapper: WebSocketConnecting {
    func connect() {
        websocket.connect()
    }

    func disconnect() {
        websocket.disconnect()
    }

    func write(string: String, completion: (() -> Void)?) {
        websocket.write(data: string.data(using: .utf8) ?? Data(), completion: completion)
    }
}
itsallmememe commented 1 year ago

To give more information we need to use WalletConnect for two reasons

So, in total can you