Written by not-bad-dev.eth
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:
- 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
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
Configuration
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.
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.
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 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)
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.
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)
}
}
Connection
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.
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.
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.
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
Top comments (0)