WalletConnect Sign v2.0: Beginner’s Guide for JavaScript Developers
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:
- Connect to a session
- Close a session
- Listen to events
- 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 standalone
version. 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:
- Connect to a session
- Disconnect a session
- Listen to events emitted from the wallet
- 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.