Beginner guide to WalletConnect Sign v2.0 for iOS Developers

WalletConnect Sign is an open protocol designed to allow secure peer-to-peer communication between dapps and wallets. Clients can live on one or two separate devices and exchange messages through JSON-RPC methods defined by the protocol.

WalletConnect Sign requests are transported over the WalletConnect Network, which relays all the messages exchanged by the clients in the ecosystem. First, clients exchange keys with the Diffie-Hellman protocol to provide end-to-end encryption. Later, symmetrically encrypted messages are sent together with matching MAC(Message Authentication Code) to grant message authenticity and data integrity.

If you are not familiar with those concepts, you can think of MACs as seals on envelopes. You trust the letter in the envelope has not been modified until the seal is broken or doesn’t match the sender. The image below should give you a grasp of what a Diffie-Hellman key exchange is:

Swift Client

In a standard WalletConnect Sign communication, you can distinguish two different types of clients:

  1. The proposer client which generates the pairing URI and usually presents it in the form of a QR code. This client will propose a session — usually a dapp.
  2. The responder client which scans the QR code or opens the deep link and approves the pairing and sessions — usually a wallet.

The Swift WalletConnect Sign SDK can be consumed by native dapps, wallets, and proxy applications.

Let’s configure WalletConnect Sign instance:

let metadata = AppMetadata(name: <String>,
description: <String>,
url: <String>,
icons: <[String]>)Sign.configure(Sign.Config(metadata: <AppMetadata>, projectId: <String>))
  1. metadata — describes your application and will define pairing appearance in a web browser.
  2. projectId- is a parameter used to access the WalletConnect Network. Go to walletconnect.com to generate one.
  3. socketConnectionType — .automatic by default. Your client’s web socket connection will be controlled automatically by the SDK, but if you want to have control over the socket connection, you can set it to .manual

After the client is configured, you can access it by `Sign.instance`. Signs instance exposes Combine publishers so you can set one or multiple subscribers to it. Publishers will call the receive(subscription:) method on all subscribers to inform your app of incoming events.

Pair the clients

You probably started wondering what a pairing is. So, this is a particular type of object that allows you to keep encrypted communication between a web browser and a wallet. You will use it to establish sessions for further communication between dapps and your wallet. That means that users don’t have to scan QR codes over and over again. Instead, they will initiate new sessions with just a single button tap. If you had an experience with WalletConnect v1.0, I am sure you feel this is an enormous UX improvement.

Ok, so we already have our Sign instance configures, and we know what a pairing is. So let’s establish one. First, you need a pairing URI, which the dapp can generate and display as a QR code. It contains necessary information required to initialize communication like the proposer’s public key or topic by which your client will know where to publish and subscribe for events of your interest.

once you have a pairing URI, you can call:

try await Sign.instance.pair(uri: uri)

Under the hood, clients settle the pairing that will live for 30 days by default and will expire after on both peers. Keep in mind that even though pairing has an important role, you do not necessarily need to list active pairings to the wallet user, as they will be more interested in sessions.

Establish a Session

Optimistically after clients settle the pairing, the dapp will propose a session with Proposal Namespaces. A session proposal is a handshake sent by a dapp, and its purpose is to define session rules. The handshake procedure is described by CAIP-25. Session.Proposal object conveys a set of required ProposalNamespaces that contains required blockchains, methods, and events. Dapp requests with methods and wallet will emit events defined in namespaces.

The user will either approve the session proposal (with session namespaces) or reject it.

Note that accounts included in the namespace body should follow CAIP10 specifications and be prefixed with a chain identifier. You can find more on blockchain identifiers in CAIP2.

in order to receive a proposal, subscribe on:

public var sessionProposalPublisher: AnyPublisher< Session.Proposal, Never>

Sign.instance.sessionProposalPublisher
   .receive(on: DispatchQueue.main)
   .sink { [weak self] sessionProposal in
   // present proposal to the user
}.store(in: &publishers)

Present proposal to the user, and when the user approves dapp’s proposal, call approve function :

Sign.instance.approve(proposalId: proposal_id, namespaces: [String: SessionNamespace])

A secure communication channel for sending payloads between wallets and dapps is provided by sessions. In WalletConnect Sign v2.0 protocol there is a significant improvement in session management. Each Session has a defined expiry date whose primary purpose is to eliminate scenarios where one of the clients is not communicating the session deletion, and its peer keeps the Session alive. By default, sessions will live for a week and then expire. But if you consider it should stay alive as the user is actively utilizing it, you can call extend function that will extend a session to the next seven days.

try await Sign.instance.extend(topic: session.topic)

Talk to your peer

We already have a pairing and a session so we can begin communicating with our peer. WalletConnect Sign protocol has a special method for doing this: wc_sessionRequest. Dapp will use it to request the wallet for transactions or messages to sign.

Requests will be delivered by the following publisher.

Sign.instance.sessionRequestPublisher
   .receive(on: DispatchQueue.main)
   .sink { [weak self] sessionRequest in
   // present session request to the user
}.store(in: &publishers)

and one can simply respond for a request with either result or an error:

let result = sign(request: sessionRequest) // implement your signing methodlet response = JSONRPCResponse<AnyCodable>(id: sessionRequest.id, result: result)Sign.instance.respond(topic: sessionRequest.topic, response: .response(response))

There are a lot of JSON RPC methods used by the Ethereum community already, and there is more to be introduced not only to Ethereum but to other blockchains as well. We wanted the WalletConnect Sign protocol to be chain agnostic and allow wallet holders to sign transactions for any chain, but we also wanted WalletConnect to stay flexible for future methods that can either be proposed by the community or be just your project’s specific method. In order to allow it in the WalletConnect Sign v. 2.0 protocol, we’ve introduced an AnyCodable type. You can wrap any type in it and send it over the network. When the request or response is received, the expected type can be easily derived from parameters or a result:

sessionRequest.params.get([EthereumTransaction].self)

What’s next?

It is important to mention that our SDK is still in its beta phase, but I am sure it is the right time to begin integration with your project. To learn more about the protocol, check out our documentation, GitHub, and join our discord server to be a part of our community. You may also like to try our example apps that are part of our repo.

Subscribe to WalletConnect
Receive the latest updates directly to your inbox.
Verification
This entry has been permanently stored onchain and signed by its creator.