How to build a wallet with WalletKit: Complete guide
Have you ever scanned a QR code to connect your wallet?
If you have, you've experienced the power of WalletKit. WalletKit is a comprehensive toolkit designed to enable seamless wallet building and connectivity. By integrating WalletKit, your wallet can connect to thousands of apps supported by the WalletConnect Network.
Now that you understand what WalletKit is, let's dive in and build a web-based wallet that can connect and sign messages using WalletKit.
Here's a quick demo of what we'll be building
Installation
To get started with WalletKit, first install the required libraries using the following command:
pnpm add @reown/walletkit @walletconnect/utils @walletconnect/core
Initialisation
After installing the required libraries, let's initialise the WalletKit instance. If you're using Next JS or another React-based framework, initialise this instance in your root file.
Let's create a provider that we'll use in the root file (layout.tsx for Next JS).
'use client';
import { Core } from '@walletconnect/core'
import { WalletKit, IWalletKit } from '@reown/walletkit'
export let walletKit: IWalletKit | null = null;
export const WalletKitInitialise = () => {
const initialiseWalletKit = () => {
try {
// Make sure you have setup ProjectID in .env
const core = new Core({
projectId: process.env.NEXT_PUBLIC_PROJECT_ID,
});
// Initialize the walletkit
walletKit = await WalletKit.init({
core,
metadata: {
name: 'Cool Wallet', // replace with your own name
description: 'Wallet to demo power of WalletKit', // replace with your desc
url: 'https://reown.com/walletkit', // replace with your url
icons: [] // add you wallet's icon URL
});
};
useEffect(() => {
if (!walletKit) {
initialiseWalletKit();
}
}, []);
return <></>
}
Now let's implement this provider in your layout file.
...
<body>
{children}
<WalletKitInitialise />
</body>
...
Session Proposals
To connect your wallet with any app, you need to create a session. This requires a URI from the WalletConnect QR modal. Once you have the URI, calling walletKit.pair({ uri }) will trigger a session_proposal event.
The walletKit instance emits various events that handle different wallet actions. When connecting your wallet to a dApp, walletKit.pair() emits a session_proposal event that you can listen to and approve to establish the connection.
Before approving a session proposal, you'll need to create approvedNamespaces using buildApprovedNamespaces. This defines which chains, methods, events, and accounts you want to use in the session.
Let's create supportedNamespaces to use it in approvedNamespaces
const supportedNamspaces = {
// You can add multiple namespaces like cosmos, near, solana, etc
eip155: {
chains: ["eip155:1", "eip155:137"],
methods: ["eth_sendTransaction", "personal_sign"],
events: ["accountsChanged", "chainChanged"],
// Replace wallet address with your address
accounts: [
`eip155:1:0x3039e4a4a540F35ae03A09f3D5A122c49566f919`,
`eip155:137:0x3039e4a4a540F35ae03A09f3D5A122c49566f919`
],
},
},
});
Listen to the session_proposal event and call walletKit.approveSession to establish a session between your wallet and the app.
useEffect(() => {
walletkit.on("session_proposal", onSessionProposal);
}, []);
const onSessionProposal = useCallback(
async (proposal: WalletKitTypes.SessionProposal) => {
try {
const approvedNamespaces = buildApprovedNamespaces({
proposal,
supportedNamespaces
});
await walletKit.approveSession({
id: proposal?.id as number,
namespaces: approvedNamespaces,
});
}
catch (error) {
console.error(error);
}
},
[]
);
You can access all active wallet sessions by calling walletKit.getActiveSessions().
Rejecting a Proposal
To reject a request, call walletKit.rejectSession with an appropriate reason, such as "user rejected".
await walletKit.rejectSession({
id: proposal.id, // You need to store proposal Id when getting a request
reason: getSdkError("USER_REJECTED")
})
Session Requests
Now, once a session is created, you can send session requests to perform actions like personal_sign, eth_sendTransaction, and more. Let's explore how to handle a personal_sign request and return a valid signature from our wallet.
The session_request event triggers when our wallet receives an action request from the app. The event contains topic and request objects that help us process and respond to the request.
Let's listen to the session_request event to handle personal_sign and return valid signature.
useEffect(() => {
walletkit.on("session_request", onSessionRequest);
}, []);
const onSessionRequest = useCallback(
async (event: WalletKitTypes.SessionRequest) => {
const { topic, params, id } = event;
const { request } = params;
// Get the message to sign
const requestParamsMessage = request.params[0];
// Convert the message to sign
const message = hextToString(requestParamsMessage);
// Sign the message
// You can use the `signMessage` method from your walletClient instance
const signature = await walletClient.signMessage({
message,
account: Account // get this from privateKey
});
// once you have signed, return the signature
await walletKit.respondSessionRequest({
topic: data.requestEvent?.topic as string,
response: {
id,
result: signature,
jsonrpc: "2.0",
},
});
}
);
To handle the eth_sendTransaction event, follow the same pattern used for personal_sign. You'll receive all necessary transaction data from params. Once you have this data, use your walletClient to execute the transaction.
Rejecting a Request
If you want to reject the session_request, you can respond with an error message using the same respondSessionRequest method.
const handleRejectRequest = async (data: WalletKitTypes.SessionRequest) => {
// Response object
const response = {
id: data?.id as number,
jsonrpc: "2.0",
error: {
code: 5000,
message: "User rejected",
},
// Respond with error response
await walletKit.respondSessionRequest({
topic: data?.topic as string,
response,
});
};
Congratulations!
You've learned how to integrate WalletKit into your wallet, enabling essential features like wallet connections, message signing, and transaction handling. Beyond these core functions, WalletKit offers Verify API, Notifications, and One Click Auth, with Chain Abstraction coming soon.
For a complete implementation example, you can refer to this sample wallet repository built with WalletKit.