How to connect your DApp to a Wallet ?
How to connect your DApp to a Wallet ?
Published the 2022-06-26
Published the 2022-06-26

Nowadays, more and more applications are using blockchain technology to provide amazing features. One of your first steps as a developer will probably be to connect your DApp (decentralized application) to a wallet to interact with the user's account.

Which wallet, which blockchain? Those are some of the questions you will probably face. WalletConnect is a powerful tool which helps you address them by allowing you to link with numerous wallet providers with the same software development kit.

The purpose of this article is to explain how WalletConnect can help you support any wallet providers, and describe how you can use it in an Android or iOS application with concrete examples.

Introduction to WalletConnect

What is a crypto wallet? How do you interact with the blockchain? 

To answer those questions, we need to understand that blockchain technologies enable the decentralization of data and executed logic. In traditional web 2.0 applications, two main options are commonly used to identify a user: either create an account (the application itself stores data such as an email and a password); or login via a third party connection (social media accounts (Facebook, Twitter, …) or your preferred email provider (Google, Microsoft, ..)). Both are centralized systems.

With a decentralized system, the user himself stores his “credentials” with so-called "private keys", inside his own crypto wallet. A private key allows you to prove ownership of a blockchain address and sign transactions. They are therefore of great value and consequently stored in software or hardware wallet to secure them. However, many different wallet providers exist (MetaMask, Trust Wallet, ...) and supporting only a few will restrict the user base of your DApp. On the other hand, it would be tedious and complex to implement the connection with all of them. This is where WalletConnect comes into play.

WalletConnect logo
WalletConnect logo

How can WalletConnect help you? How does it work?

WalletConnect helps DApps connect to wallets. How? By acting as a middleman between your Dapp and the wallet of your user. It greatly eases software development, as you need to implement only once the connection with WalletConnect to support more than 170 different wallet providers.

WalletConnect acts as a middleman between the DApp and the user's wallet.
WalletConnect acts as a middleman between the DApp and the user's wallet.

To establish a connection between all the actors, you first need to connect the DApp with the middleman server. With WalletConnect V1, the middleman server is called a “bridge server”, and its address is bridge.walletconnect.org. With WalletConnect V2, the middleman is called the “relay server” and its address is relay.walletconnect.com. The second step is to connect the user’s wallet with the bridge/relay server, with the DApp running on a desktop or a mobile device, and the wallet being on the mobile device. Several combinations are possible and listed in the table below.

Compatibility table for wallet connection between a DApp and a wallet.
Compatibility table for wallet connection between a DApp and a wallet.

In this article, we cover the mobile-mobile connection with Android and iOS. The next section details it with a practical example. The connection process between your DApp and the relay/bridge server is started by your DApp, by displaying a QR-Code or opening a deep link. Then, the user will need to process the request by choosing his favorite wallet application.

The wallet application should automatically process the URI and communicate with the relay/bridge server. After a connection is established between the wallet and the relay/bridge server, a callback is sent by the relay/bridge server to notify the DApp that the connection is established. Your DApp is now ready to interact with the blockchain, process transactions, ... with the user's wallet. The figure below summarizes all the required steps to connect your DApp with the user's wallet.

Complete connection process flow between a DApp and a user's wallet using WalletConnect.
Complete connection process flow between a DApp and a user's wallet using WalletConnect.

Practical example

In this section, a practical example is shown with concrete code examples. The target is a mobile-mobile connection for iOS and Android applications, using WalletConnect V1 (V2 currently being in beta). The full code of both working applications can be found on our GitHub for iOS and Android.

Here you can find the android case and here the iOS case.

Android

Configuring the project

Add in your settings.gradle the jitpack repository.

//setting.gradle

dependencyResolutionManagement {

   …

   repositories {

       maven { url "https://jitpack.io" }

   }

}

Then add the following dependencies to your app/build.gradle.

// app/build.gradle

dependencies {

...

implementation 'com.github.WalletConnect:kotlin-walletconnect-lib:0.9.6'

implementation 'com.github.komputing:khex:1.1.2'

implementation 'org.java-websocket:Java-WebSocket:1.4.0'

implementation 'com.squareup.moshi:moshi-kotlin:1.13.0'

implementation 'com.squareup.moshi:moshi:1.13.0'

implementation 'com.squareup.okhttp3:okhttp:4.9.3'

}

Connecting the DApp with the bridge server

First, set a few static constants defining your DApp and the bridge, and a few variables which will be used to connect with WalletConnect.

companion object {

       private lateinit var client: OkHttpClient

       private lateinit var moshi: Moshi

       private lateinit var storage: WCSessionStore

       private lateinit var config: Session.Config

       private lateinit var session: Session

       private val bridgeUrl = "https://bridge.walletconnect.org"

       private val metaData = Session.PeerMeta(

              name = "nfscene",

              url = "nfscene.com",

              description = "nfscene WalletConnect demo"

       )

}

Then, you will need to initialize the HTTP client (OkHttpClient), the JSON parser (Moshi), and the storage that stores the WalletConnect session.

client = OkHttpClient.Builder().build()

moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()).build()

storage = FileWCSessionStore(

       File(

           getContext().cacheDir,

           "session_store.json"

       ).apply { createNewFile() }, moshi

)

You are now ready to open a session with the bridge server.

fun resetSession(callback: Session.Callback) {

   nullOnThrow { session }?.clearCallbacks()

   val key = ByteArray(32).also { Random().nextBytes(it) }.toNoPrefixHexString()

   config = Session.Config(UUID.randomUUID().toString(), bridgeUrl, key)

   session = WCSession(

       config,

       MoshiPayloadAdapter(moshi),

       storage,

       OkHttpTransport.Builder(client, moshi),

       metaData

   )

   session.addCallback(callback)

   session.offer()

}

The callback argument is a class which needs to implement the Session.Callback interface. Two signature need to be implemented. The fun onStatus(status: Status) is called when a connection change occurs. After the session.offer() method is called, the DApp will try to connect with the relay server. If it succeeds, the callback is done and you will receive the status: Session.Status.Connected .

Connecting the wallet with the bridge server

You are now able to connect the wallet with the relay server. To do so, we will use a mobile deeplink in order to call an application that can handle the request. The deeplink can be found in the config object. Here is the method to call the deeplink:

fun signIn() {

   val intent = Intent(Intent.ACTION_VIEW, Uri.parse(config.toWCUri()))

   intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)

   getContext().startActivity(intent)

}

The user will then choose his favorite wallet application to handle the request. It will not work if there are no wallet applications installed on the device.

After the mobile deeplink is opened by a wallet application, the user will be prompted for confirmation.
After the mobile deeplink is opened by a wallet application, the user will be prompted for confirmation.

Here is an example of a confirmation dialogue generated by the MetaMask mobile application. After the user has accepted the connection, the wallet activity will be closed and your DApp activity will come back. The callback method will be called with the status: Session.Status.Approved .

A full connection pipeline is now achieved, your requests can then be processed through the WalletConnect bridge. If you want to fetch the accounts, you can call the session.approvedAccounts() method. To close the session, simply call the session.kill() method.

The complete Android DApp example can be found in the GitHub repository.

iOS

Configuring the project

To use the WalletConnect SDK inside an iOS application, you will need to add the WalletConnectSwift package to your XCode project. Click on File > Add Packages... then paste the WalletConnectSwift GitHub repository URL inside the search bar, and click on Add Package.

Add the WalletConnectSwift package to your XCode project.
Add the WalletConnectSwift package to your XCode project.

Then, one specific permission must be given to the XCode project to allow the demo application to open mobile deeplinks. To do so, add the following LSApplicationQueriesSchemes to the info.plist of your project.

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">

<plist version="1.0">

<dict>

<key>LSApplicationQueriesSchemes</key>

<array>

<string>wc</string>

</array>

</dict>

</plist>

Connecting the DApp with the bridge server

First, you need to set a few static constants: define your client information using a Session.ClientMeta struct, and generate a 32-bytes unique key. Both required to define your DApp information (inside a Session.DAppInfo struct).

You will also need a "delegate" following the ClientDelegate protocol to receive callback calls regarding the connection status. In our case, the class itself implements the ClientDelegate protocol.

let clientMeta = Session.ClientMeta(name: "WalletConnectDemo",

description: "A mobile application acting as a Dapp to connect to a crypto wallet using the WalletConnect SDK",

icons: [],

url: URL(string: "https://nfscene.com")!)

let dAppInfo = Session.DAppInfo(peerId: UUID().uuidString, peerMeta: clientMeta)

client = Client(delegate: self, dAppInfo: dAppInfo)

Before initiating the connection with the bridge server, you will need to define the URL of the bridge server you wish to connect to, using a WCURL struct. Finally, the connection with the bridge server can be initiated using the connect(to url: WCURL) method.

let wcUrl = WCURL(topic: UUID().uuidString,

bridgeURL: URL(string: "https://bridge.walletconnect.org/")!,

key: try! generateRandomKey())

try! client.connect(to: wcUrl)

If the connection between the DApp and the bridge server is successful, the previously defined delegate will receive a call to the client(_ client: Client, didConnect url: WCURL) method.

Connecting the wallet with the bridge server

After a successful connection between the DApp and the bridge server, the next step is to connect the user's wallet and the bridge server. To do so, create a mobile deeplink using the connecting URL previously created. Then, open the mobile deeplink using the UIApplication framework.

let connectionUrl = wcUrl.absoluteString

let deepLinkUrl = "wc://wc?uri=\(connectionUrl)"

DispatchQueue.main.asyncAfter(deadline: .now() + 1) {

if let url = URL(string: deepLinkUrl), UIApplication.shared.canOpenURL(url) {

UIApplication.shared.open(url, options: [:], completionHandler: nil)

} else {

print("Failed to open deepLinkUrl \(deepLinkUrl)")

}

}

Note: Unlike Android, iOS only allows a single application to open deeplinks of a certain schema. Unfortunately, this means that the user cannot choose which wallet he wants to open. The last installed wallet application supporting the schema will be opened. For the purpose of this demo, we recommend the MetaMask wallet installed on the device. We will release a future article explaining how to mitigate this issue and allow the user to choose which wallet he wants to open.

Depending on the wallet application of the user, a confirmation dialogue will be opened and the user will be asked for confirmation. Here is an example of the MetaMask confirmation dialogue on an iOS device.

Confirmation dialogue inside the MetaMask application, triggered by the opening of a WalletConnect deeplink.
Confirmation dialogue inside the MetaMask application, triggered by the opening of a WalletConnect deeplink.

After the user has confirmed the connection, the DApp will be opened again, and the previously defined delegate will receive a call to the client(_ client: Client, didConnect session: Session) callback method.

The user's wallet information will be contained inside the session.walletInfo structure. To close the session, the disconnect(from session: Session) method should be called on the previously defined Client.

A full connection pipeline is now achieved between your DApp and the user's wallet. Requests can now be processed through the WalletConnect bridge.

The complete iOS DApp example can be found in this GitHub repository.

Conclusion

More and more different wallet providers are being created, and it will be challenging for you as a DApp developer to keep up with the main actors on the market. WalletConnect solves this problem with a clean, simple and effective solution by acting as a middleman between your DApp and any wallet provider.

At nfscene, we really appreciate this solution as it allows more wallets to be supported, and eases the development process. WalletConnect is a perfect technology example of what we stand for, the democratization of the blockchain.

However, there are always downsides and trade-offs. The use of a third-party bridge server might be an issue from a security standpoint, and therefore we advise using read-only transactions. This critical point can also be addressed by creating your own bridge server.