WalletConnect Sign v2.0: Beginner’s Guide for JavaScript Developers

Vanes
WalletConnect
Published in
8 min readNov 22, 2022

Note: If you are a wallet developer and looking to integrate WalletConnect, please use the latest version of our tooling — the Web3Wallet SDK. For more information, you can refer to our docs.

We’re excited to introduce the WalletConnect v2.0 Sign SDK! WalletConnect Sign is a remote protocol designed for secure peer-to-peer communication between dApps (web3 applications) and wallets.

WalletConnect Sign establishes a remote pairing between two devices using a relay server. The payloads from these relay servers are symmetrically encrypted with a shared private key between the peers.

The Sign SDK greatly enhances the user experience of dApps. The pairing can be initiated seamlessly by scanning a QR code displayed by a peer and approving the pairing request.

If you are interested in knowing more about the upcoming features and latest information on our protocols, check out our technical specifications.

Getting started with the JavaScript client

In today’s guide, we’re going to be building a React app that can:

  1. Connect to a session
  2. Close a session
  3. Listen to events
  4. Send a transaction

You can follow along step by step, commit by commit here if you get lost. You’re welcome!

Step 1: Install Dependencies

The first step is to create a React app. Open your terminal, create the app and install the dependencies.

Let’s look at what dependencies you will be installing:

@walletconnect/sign-client
A protocol establishes a remote pairing between two apps and/or devices

@web3modal/standalone
In this example, since we won’t be using wagmi, we want to use the standaloneversion. To view other available versions, see our docs.

npx create-react-app sign-v2-standalone
cd sign-v2-standalone
npm install @walletconnect/sign-client @web3modal/standalone

Open the project in your favorite code editor.

Step 2: Add Project ID

If you don’t have a Project ID yet, head over to the WalletConnect Cloud to create a project and copy its unique Project ID.

I’m going to store my projectId in an environment variable. To do that, I’m going to create a new file called .env and paste in the following code. Use the Project Id number from above. Don’t forget to add .env to .gitignore.

REACT_APP_PROJECT_ID=<YOUR_PROJECT_ID>

Step 3. Create Client

In this step, we’re going to initialize the Sign client. We then will save that instance to a state variable.

Go into App.js and remove logo from imports and everything in the App-header div.

import "./App.js";

function App(){
return(
<div className="App">
</div>
)
}

Add the following imports.

import { useState, useEffect } from "react";
import { SignClient } from "@walletconnect/sign-client";

Next, we’re going to create the state variable where we will save the instance of the client.

const [signClient, setSignClient ] = useState();

Create an async function called createClient. To handle errors we may get, begin with a try/catch block. Inside the try block, initialize SignClient by calling init, and pass an object containing the projectId.

The init method returns a promise that resolves to a SignClient instance, which is then stored in the signClient variable using the setSignClient function.

async function createClient(){
try{
const signClient = await SignClient.init({
projectId: process.env.REACT_APP_PROJECT_ID
})
setSignClient(signClient);
} catch(e) {
console.log(e);
}
}

Create a new useEffect hook. We’re going to check if signClient exists, if it doesn’t, we’re going to call createClient to create an instance. Don’t forget to pass signClient into the dependency array.

useEffect(() => {
if(!signClient){
createClient();
}
}, [signClient])

Let’s add a title to the page. In the return statement, add the following.

<h1>Sign v2 Standalone</h1>

How App.js should look like after this step.

Step 4. Open Web3Modal

In App.js add the following import.

import { Web3Modal } from "web3modal/standalone";

The projectId is used to identify the project that is using the Web3Modal utility, and the standaloneChains property is used to specify which Ethereum chains the Web3Modal instance should connect to.

Outside of App, create a new Web3Modal instance by passing in an object containing the projectId and the standaloneChains property. The standaloneChains property used to specify which Ethereum chains the Web3Modal should connect to.

const web3Modal = new Web3Modal({
projectId: process.env.REACT_APP_PROJECT_ID,
standaloneChains: ["eip155:5"]
})

Create a new async function called handleConnect. Let’s first check that signClient exists, if it doesn’t, an error is thrown. Continue with a try/catch block.

To connect to the blockchain, we will use the connect method provided to us from SignClient. This method returns a promise that resolves to an object containing the uri property. In the method, pass in an object containing requiredNamespaces. The required namespace defines the capabilities of the client and the actions it is allowed to perform.

The uri is then destructured from the returned object. The uri is a unique identifier for a WalletConnect session. It is a string that contains information about the session. We check to to see if it exists, if it does, we open the modal.

To open the modal, you call openModal and pass in uri.

async function handleConnect(){
if (!signClient) throw Error("SignClient does not exist");

const proposalNamespace = {
eip155: {
methods: ["eth_sendTransaction"],
chains: ["eip155:5"],
events: ["connect", "disconnect"]
}
}

const { uri } = await signClient.connect({
requiredNamespaces: proposalNamespace
});

if (uri){
web3Modal.openModal({ uri });
}

try {
} catch(e){
console.log(e);
}
})

To test the function, after the header text, add a new button. Add a disabled attribute to the element and set it to !signClient so if signClient doesn’t exist, the button is disabled.

<button onClick={handleConnect} disbaled={!signClient}>Connect</button>

How App.js should look like after this step.

5. Display Account Number

In this step, we’re going to save the session and account information to state variables. Then we’re going to display the user’s account information.

In order to connect our dApp to wallet, we need to use a wallet that has Sign v2 integrated. For this example, we are going to use WalletConnect’s demo wallet. This is a testing wallet, so please don’t store real funds. In order to access testnets, click on the Settings gear and enable Testnets.

Begin by adding two new state variables.

const [session, setSession] = useState([]);
const [account, setAccount] = useState([]);

Next, we’re going to destruct another property, approval from the returned object.

const { uri, approval } = await signClient.connect({
// ...
})

The approval property specifies the session namespace that the user has approved for the connection. After opening the modal, we can save the session namespace by awaiting the approval call. Then we pass that session to onSessionConnected (we will be creating this function next). Lastly, you probably noticed once you scanned the QR code, the modal stayed open. We can fix that by calling closeModal.

const sessionNamespace = await approval();
onSessionConnected(sessionNamespace);
web3Modal.closeModal();

Create a new async function called onSessionConnected and pass in session as a parameter. Start with a try/catch block. In here you will set the session to the session parameter and you’ll set account to the account number.

async function onSessionConnected(){
try {
setSession(session);
setAccount(session.namespaces.eip155.accounts[0].slice(9));
} catch(e){
console.log(e)
}
}

Let’s render our account information. In your return statement, after your title, create a ternary expression. If the account array is empty, then render the connect button. If the account array isn’t empty, then render the account value.

{account.length ? (
<p>{account}</p>
) : (
<button onClick={handleConnect} disabled={!signClient}>
Connect
</button>
)}

How App.js should look like after this step.

Step 6. Disconnect Session

Create a new async function called handleDisconnect. In a try/catch block, call the disconnect method from signClient. Pass in an object that includes the topic, message, and code. To view all the available error codes, take a look at our docs.

After awaiting the disconnect call, call a new function called reset.

await signClient.disconnect({
topic: session.topic,
message: "User disconnected",
code: 6000,
});
reset();
} catch (e) {
console.log(e);
}
}

Now lets create that new function. reset will just set account and session to an empty array.

 const reset = () => {
setAccount([]);
setSession([]);
};

Update your return statement to include a button to disconnect.

<>
<p>{account}</p>
<button onClick={handleDisconnect}>Disconnect</button>
</>

Go ahead and test your button.

How App.js should look like after this step.

Step 7. Subscribe to Events

What if the user disconnects a session from their wallet. How can your app listen to that event? Here’s how. Create another async function called subscribeToEvents and pass client as a parameter. Check if the client exists, if it doesn’t then throw an error. Inside the try block, call the on method, pass in the event you’re listening to for. To test, we’re going to log a message so that when the user disconnects the session from their wallet, we should see the logged message from the dApp. After logging, call reset.

async function subscribeToEvents(client) {
if (!client)
throw Error("Unable to subscribe to events. Client does not exist.");
try {
client.on("session_delete", () => {
console.log("The user has disconnected the session from their wallet.");
reset();
});
} catch (e) {
console.log(e);
}
}

Now we need to call this new function we just created. In createClient, after setting signClient, await for subscribeToEvents and pass in signClient as an argument.

How App.js should look like after this step.

Step 8. Log Result

Next, we’re finally going to send a transaction. Let’s create a new async function called, handleSend. This function will handle the send request.

Check to see if account has any items, if it doesn’t, throw an error.

Continue with a try/catch block. We’re going to begin with setting up the params of the transaction. To view a sample object, check out our docs.

The tx is going to give us the details of the transaction. Since we want to send from whatever account we are connected to, we set from to account. Feel free to change the to address. data, gasPrice, gasLimit, and value all need to be encoded in hex or else you will get a failed transaction.

const tx = {
from: account,
to: "0xBDE1EAE59cE082505bB73fedBa56252b1b9C60Ce",
data: "0x",
gasPrice: "0x029104e28c",
gasLimit: "0x5208",
value: "0x00",
};

Next, we’re going to save and log the result.

const result = await signClient.request({
topic: session.topic,
chainId: "eip155:5",
request: {
method: "eth_sendTransaction",
params: [tx],
},
})

console.log(result);

In your return statement, after the account details, let’s add a new button to send the transaction.

<button onClick={handleSend}>Send Transaction</button>

Test the button. Accept the transaction from the wallet and wait for the logged result. The logged response is the transaction hash, so if you go to https://goerli.etherscan.io/tx/${result} , you should be able to see your new transaction.

How App.js should look like after this step.

Step 9. Display the transaction URL

We’re going to update the UI so that we can see the result on the page instead of just logging it.

Start with creating a new state variable. You can call it txnURL.

const [txnUrl, setTxnUrl] = useState();

Replace the log you make in the last step, with this. We’re going to save and set the result to txnUrl.

setTxnUrl(result);

In your return statement, following the Disconnect button, add the following. If txnUrl exists, then render some header text with the Etherscan transaction information.

{txnUrl && (
<h2>
Check out your transaction{" "}
<a
href={`https://goerli.etherscan.io/tx/${txnUrl}`}
target="_blank"
>
here
</a>
!
</h2>
)}

How App.js should look like after this step.

Recap

To recap what you’ve just built. You’re created a dApp that was able to:

  1. Connect to a session
  2. Disconnect a session
  3. Listen to events emitted from the wallet
  4. Send a transaction

Where to go from here?

We encourage you to check out our GitHub and documentation if you are interested in learning more about our protocol.

If you want a more robust example, checkout our sample dapp. We have 9 chains, including Solana. For other JavaScript web examples, visit our docs.

Make sure to follow WalletConnect on Twitter to stay informed about our upcoming announcements and updates. If you have any questions, please feel free to shoot them out on our Discord server.

--

--