Wallets are an essential part of the blockchain. They function as a way of storing our web3 assets, as a form of identity, for authentication among others. When building a Dapp knowing how to work with wallets is crucial since it is a big part of the user experience. One area that has not been covered as much is working with wallets when building mobile Dapps and in since our main goal at Weedle is promoting mobile first Dapp development, this seems like a great topic to write about.
The aim of this tutorial is to cover everything you need to know to effectively add the ability to use wallets to your mobile Dapps. The tutorial will be distributed into parts, with each part taking some more advanced topics than the last.
First Some Dependencies
I'm assuming you already have your app ready, if you don't head over to this article to learn how to setup a react native app that is web3 compatible.
The walletconnect documentation for react native can be found here. According to the docs we need three dependencies, you can go ahead and copy the code below to install them
yarn add @walletconnect/react-native-dapp react-native-svg @react-native-async-storage/async-storage
However there is a small issue with this, the types in the async storage library (@react-native-async-storage/async-storage) specified are incompatible with walletconnect. In fact using it directly with a typescript setup will show you an error saying the type is incompatible with walletconnect.
There are a couple of solutions:
Option 1
One option (long and difficult way) would be to get the types walletconnect expects and write an adapter that maps to those types. If you want to go via this route, you can find the file with walletconnect typings in a seperate utils github repo here. You can basically create a file and put the types inside it, then use it as the type for your adapter.
Option 2(My preferred option)
Is fast and dirty. Basically examining the keyvaluestorage functions from walletconnect you will see that they are not only making use of a very small set of functions from the async storage library. To get the correct types we can use the types referred to in option 1 and just cast it to the asyncStorage props from walletconnect provider.
I prefer this method since walletconnect isn't using any significant number of functions from the library, so mapping all functions just because a few are being used doesn't make sense to me. This can be seen as a temporary measure at least till this issue has been fixed by the WalletConnect team.
Integrate WalletConnect with our App
According to the docs we have two ways of using WalletConnect. We can either use it as a provider or as a Higher order component; lets stick with the provider method. The provider takes a couple of props, but only two of them are compulsory, there are default values provided for the other props which is sufficient for our use case.
Say our app currently looks like this
<View style={styles.mainContainer}>
<MainApp />
</View>
To enable wallectconnect, we can just need to wrap the app in its provider like so
<WalletConnectProvider
redirectUrl={`wmw://app`} // Explained below
storageOptions={{
asyncStorage: AsyncStorage as unknown as IAsyncStorage,
}}>
<View style={styles.mainContainer}>
<MainApp />
</View>
</WalletConnectProvider>
The value in the storageOptions was explained earlier, as for the redirectUrl this is a deep link url that walletconnect redirects to after validation. More on that here. My preferred method of handling the redirect url would be to redirect to a view where I can verify if the user has been authenticated or not, preferrably back to the current page where the user tried to login using their wallet.
Now that we have wrapped our app with the walletconnect provider, lets look at how we can authenticate using walletconnect. To explain this, lets say inside MainApp we have two components, Auth and Dashboard. We want to only show dashboard after authenticating the user
// Auth.tsx
import React, {useState} from 'react'
import WalletConnectProvider, { useWalletConnect } from '@walletconnect/react-native-dapp';
...
const Auth = () => {
const [isConnected, setIsConnected] = useState<boolean>(false);
const connector = useWalletConnect(); // Wallet connect hook
...
// Note: for example purposes only, do better and split your components into files properly
return (
<View>
{
!isConnected ?
<View>
<Button title='Login' onPress={authenticateUser} />
</View> :
<Dashboard />
}
</View>
)
}
In the code above, we are doing a couple of things.
useWalletConnect - This hook provides a connector object for us which contains all the functions that we need to use walletconnect.
isConnected - A state variable we can use to determine if the user has been authenticated or not.
We are also using a simple button to handle calling a auth function authenticateUser which we will define next.
// Auth.tsx
// Our login function looks like this
const authenticateUser = async () => {
if (connector.connected) {
setIsConnected(true)
}else{
const session = await connector.connect();
// The session object will contain details about the chain you are connected to and also an accounts array
setIsConnected(true)
}
}
Here we are first checking if we have already authenticated with the wallet, if that isn't the case then we call .connect to take us through the authentication process with our wallet. What the process looks like is that walletconnect checks for all wallets on our device and displays them in a bottom sheet component, we can then select the specific wallet we want to use (e.g. Metamask, Trust wallet etc). An intent is then created which takes us to the selected wallet for approval, once we approve we are directed back the redirectUrl we provided to walletconnect.
The authenticate call returns a ISessionStatus type which contains details about the network we are connected to like chainId and also an accounts array which contains our wallet address from the connected wallet.
The connector object can be retrieved from the useWalletConnect hook at anytime and in any component inside our app. The object contains all we need like our accounts and network information, so we don't really need to store what we get from calling .connect
Handling Errors
Awesome, we are almost done but we need to handle some errors, like if the user rejects the auth request. This can be done by wrapping our call to .connect in a simple try catch block like this
// Auth.tsx
// Our login function looks like this
const authenticateUser = async () => {
try{
if (connector.connected) {
setIsConnected(true)
}else{
const session = await connector.connect();
// The session object will contain details about the chain you are connected to and also an accounts array
setIsConnected(true)
}
}catch(e){
// handle error here
}
}
Logout Functionality
Say on our lovely dashboard we have a logout button which allows the user disconnect from their wallet from our app.
// Dasboard.tsx
const Dashboard = async () => {
...
const logUserOut = async () => {
await connector.killSession();
}
return (
<View>
<Button title='Logout' onPress={logUserOut} />
...
</View>
}
Conclusion
Awesome stuff, we have been able to build a dapp that can authenticate using walletconnect. You should explore the other options from the walletconnect docs like reducing the number of wallets available for auth among others.
Although this is a pretty good starting point but there are several other things we need to do to have a stable and working dapp.
If you enjoyed this articale or it has been helpful to you in any way. Follow us here and on twitter to get notified when the follow up articles gets published. Cheers
At weedle labs, we are working on a mobile first web3 infrastructure. The aim is to simplify developing web3 Dapps on mobile and help you reach a much wider audience than what the web can offer. We are open source and are actively looking for contributors to join us in active development. Give us a shout on twitter @weedle_app or tech@joinweedle.com.
If you also wish to know when we launch and get more details, you can check https://joinweedle.com.
Top comments (1)
Great tutorial, look forward to part 2