WalletConnect Auth: how to connect a crypto wallet to iOS Swift DApp
Published: 2024-01-04 18:59:34

Written by [not-bad-dev.eth](https://twitter.com/not_bad_dev)

## Intro

This is the second article in a series of WalletConnect usage on iOS. Link to previous one. Third one is coming soon. 

**What is Auth protocol?** Wallet connect definition from [documentation](https://docs.walletconnect.com/api/auth/overview):  

- *WalletConnect Auth is an authentication protocol that can be used to log-in blockchain wallets into apps. With a simple and lean interface, this API verifies wallet address ownership through a single signature request, realizing login in one action. It enables apps to set up a decentralized and passwordless onboarding flow.*

So it’s one-step way to identify user’s wallet. It may seems like a better solution than Sign protocol but it doesn't allow handle connected sessions and call transactions on user’s wallet. 

It would be better to think about possible use cases for your application before choosing wallet connection protocol. 

## Table of Contents

- [Installation](#installation)
- [Configuration](#configuration)
- [Wallet connection](#wallet-connection)

<h2 id="installation">Installation</h2>

Add WalletConnect SDK to your app. Link to installation process is below. It may be added using SPM or CocoaPods. 

> https://github.com/WalletConnect/WalletConnectSwiftV2#installation
> 

<h2 id="configuration">Configuration</h2>

To configure the WalletConnect SDK with your app information, you need to provide the project ID. You can register for the project ID by connecting here with your crypto wallet.

> https://cloud.walletconnect.com/sign-in
> 

Provide your `projectId` to Networking configuration. Fill the project metadata with your projects name and links. Such modules as `DefaultSocketFactory` and `DefaultCryptoProvider` are described below. 

```swift
import Foundation
import WalletConnectNetworking
import WalletConnectPairing
import Auth

func configure() {
        Networking.configure(projectId: "your-project-id", socketFactory: DefaultSocketFactory())
        Auth.configure(crypto: DefaultCryptoProvider())

        let metadata = AppMetadata(
            name: "WalletConnectDemo",
            description: "WalletConnectDemo",
            url: "https://walletconnect.org",
            icons: []
        )

        Pair.configure(metadata: metadata)
    }
```

Additionally, there is a `WalletConnectSocketFactory` class that you need to implement yourself. The WalletConnect SDK doesn’t handle socket management on its own.

The easiest way is to use [Starscream SDK](https://github.com/daltoniam/Starscream) of 3.1.2 version which completley complies with their `WebSocketConnecting` protocol. (yep, it’s not new and may have some issues but it works)

```swift
import Foundation
import WalletConnectSign
import Starscream

struct WalletConnectSocketFactory: WebSocketFactory {
    func create(with url: URL) -> WebSocketConnecting {
				let socket = WalletConnectStarscreamSocket(url: url)
        let queue = DispatchQueue(label: "com.walletconnect.sdk.socket", attributes: .concurrent)
        socket.callbackQueue = queue
        return socket
    }
}

class WalletConnectStarscreamSocket: WebSocket, WebSocketConnecting { }
```

It also possible to write your own sockets implementation using `URLSessionWebSocketTask` or any other library.

Unlike the previous article about Sign protocol here we have new `DefaultCryptoProvider` class which is additionally required for Auth protocol. It’s copy paste form WalletConnect demo example project. Don’t forget to add listed libraries and be careful! Here is used forked `Web3.swift` lib from WalletConnect repo. It’s also form the demo project. 

> https://github.com/WalletConnect/Web3.swift
> 

```swift
import Auth
import CryptoSwift
import Foundation
import Web3

struct DefaultCryptoProvider: CryptoProvider {
    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)
    }
}
```

<h2 id="wallet-connection">Connection</h2>

You need to define `RequestParams` wiht own data, create pairUri wich contains uniquie identifier and send request to WalletConnect. Request parameters correspond to `CAIP-74 Cacao` object. 

```swift
func connect() async throws {
        let params = RequestParams(
            domain: "https://your-best-site.com",
            chainId: "eip155:1",
            nonce: "777",
            aud: "https://your-best-site.com",
            nbf: nil,
            exp: nil,
            statement: "Sign this message to verify your wallet and connect to Surreal.",
            requestId: nil,
            resources: nil
        )

        do {
            let pairUri = try await Pair.instance.create()
            self.pairUri = pairUri
            try await Auth.instance.request(params, topic: pairUri.topic)
        } catch {
            throw error
        }
    }
```

Next step is to redirect to users wallet using provided URL structure where with `/wc` route and `uri` parameter which contains previously create `pairUri`. Note that it must be percent encoded so use only `deeplinkUri` property which follows this rule.
**Note:** here used Rainbow wallet link. You can replace it with your preferrable wallet’s link.

```swift
func redirectToWallet() async {
        guard let deeplinkUrl = pairUri?.deeplinkUri,
              /*
              here we use rainbow because metamask dosen't support
              auth protocol
               */
              let url = URL(string: "https://rnbwapp.com/wc?uri=\(deeplinkUrl)") else { return }

        await MainActor.run(body: {
            UIApplication.shared.open(url)
        })
    }
```

And the last step is handle wallets response. You should subscribe onto `authResponsePublisher` before redirect to a wallet. Here we have simple `result` property where you can retrieve users wallet address and signature hash. 

```swift
var publishers = Set<AnyCancellable>()

func setupObservers() {
        Auth.instance.authResponsePublisher.sink { [weak self] _, result in
            guard let self else { return }

            Task { @MainActor in
                switch result {
                case .success(let cacao):
                    do {
                        let address = try DIDPKH(did: cacao.p.iss).account.address
                        self.onWalletAuthorized?(address)
                    } catch {
                        print(error.localizedDescription)
                    }
                case .failure(let error):
                    print(error.localizedDescription)
                }
            }
        }
        .store(in: &publishers)
    }
```

That’s it! You’ve successfully connected and authenticated users wallet and can continue. Note that Auth protocol works fine for one-time user authentication but you won't be able to ask user for transactions signing. Sign protocol is better if you will need to send transactions periodically.  

Good luck! Fell free to contact me and leave your feedback. 

**Follow me** on [X/Twitter](https://twitter.com/not_bad_dev)