@tomo-inc/wallet-connect-protocol
v0.0.17
Published
WalletConnect protocol SDK for Tomo Wallet - Pure JavaScript
Readme
@tomo-inc/wallet-connect-protocol
WalletConnect Protocol SDK for Tomo Wallet - Pure JavaScript / Framework-Agnostic
📖 Introduction
@tomo-inc/wallet-connect-protocol is a lightweight, framework-agnostic WalletConnect SDK designed specifically for the Tomo Wallet ecosystem. It provides a clean JavaScript API for integrating WalletConnect protocol into any application, regardless of the framework you use.
✨ Features
- 🚀 Framework-Agnostic - Works with vanilla JavaScript, Vue, Angular, Svelte, React, or any other framework
- 🌐 Multi-Chain Support - Supports Ethereum, Solana, Aptos, Cosmos, and more
- 📱 QR Code Generation - Built-in QR code generation with multiple format support
- 🔗 URI Management - Automatic generation and management of WalletConnect URIs
- 📦 TypeScript Support - Full type definitions
- ⚡ Lightweight - Minimal dependencies, no framework overhead
- 🎯 Simple API - Clean, intuitive API design
📦 Installation
# Using pnpm
pnpm add @tomo-inc/wallet-connect-protocol
# Using npm
npm install @tomo-inc/wallet-connect-protocol
# Using yarn
yarn add @tomo-inc/wallet-connect-protocol🚀 Quick Start
1. Get Project ID
First, obtain a free Project ID from WalletConnect Cloud.
2. Create Client
import { WalletConnectClient } from "@tomo-inc/wallet-connect-protocol";
const client = new WalletConnectClient({
projectId: "YOUR_PROJECT_ID",
metadata: {
name: "My DApp",
description: "My awesome decentralized application",
url: "https://myapp.com",
icons: ["https://myapp.com/icon.png"],
},
});
// Initialize the client
await client.initialize();3. Connect
// Create connection and get URI
const uri = await client.connect();
// Generate QR code
const qrCodeBase64 = await client.generateQRCode(uri, {
width: 300,
margin: 4,
});
// Display QR code
document.getElementById("qr-code").src = qrCodeBase64;4. Listen to Events
// Listen for session approval
client.on("session_update", (session) => {
console.log("Session approved:", session);
// Handle successful connection
});
// Listen for disconnection
client.on("session_delete", (data) => {
console.log("Session disconnected:", data);
});📚 API Documentation
WalletConnectClient
Core client class providing all WalletConnect functionality.
Constructor
new WalletConnectClient(config: WalletConnectConfig)WalletConnectConfig:
interface WalletConnectConfig {
projectId: string; // WalletConnect Project ID
metadata: {
name: string; // App name
description: string; // App description
url: string; // App URL
icons: string[]; // App icons
};
relayUrl?: string; // Optional relay server URL
}Methods
initialize(): Promise<void>
Initialize the WalletConnect client. Must be called before any other operations.
await client.initialize();connect(params?: ConnectParams): Promise<string>
Create a pairing connection and generate WalletConnect URI.
const uri = await client.connect();
// With custom namespaces
const uri = await client.connect({
requiredNamespaces: {
eip155: {
methods: ["eth_sendTransaction", "personal_sign"],
chains: ["eip155:1"],
events: ["chainChanged", "accountsChanged"],
},
},
});generateQRCode(uri: string, options?: QRCodeOptions): Promise<string>
Generate QR code as Base64 data URL.
const qrCode = await client.generateQRCode(uri, {
width: 300,
margin: 4,
errorCorrectionLevel: "M",
color: {
dark: "#000000",
light: "#ffffff",
},
});QRCodeOptions:
interface QRCodeOptions {
width?: number; // QR code width (default: 300)
margin?: number; // Margin size (default: 4)
errorCorrectionLevel?: "L" | "M" | "Q" | "H"; // Error correction (default: 'M')
color?: {
dark?: string; // Dark color (default: '#000000')
light?: string; // Light color (default: '#ffffff')
};
}generateQRCodeToCanvas(canvas: HTMLCanvasElement, uri: string, options?: QRCodeOptions): Promise<void>
Generate QR code directly to a canvas element.
const canvas = document.getElementById("qr-canvas") as HTMLCanvasElement;
await client.generateQRCodeToCanvas(canvas, uri, {
width: 300,
margin: 4,
});getActiveSessions(): SessionInfo[]
Get all active WalletConnect sessions.
const sessions = client.getActiveSessions();
sessions.forEach((session) => {
console.log("Session:", session.topic);
console.log("Peer:", session.peer.metadata.name);
console.log("Chains:", session.namespaces);
});disconnectSession(topic: string): Promise<void>
Disconnect a specific session.
await client.disconnectSession(sessionTopic);sendRequest(params): Promise<any>
Send JSON-RPC request to the connected wallet.
const result = await client.sendRequest({
topic: sessionTopic,
chainId: "eip155:1",
request: {
method: "personal_sign",
params: ["0x...message", "0x...address"],
},
});on<T>(event: WalletConnectEvent, handler: EventHandler<T>): void
Listen to WalletConnect events.
client.on("session_proposal", (proposal) => {
console.log("Session proposal:", proposal);
});
client.on("session_update", (session) => {
console.log("Session updated:", session);
});
client.on("session_delete", (data) => {
console.log("Session deleted:", data);
});
client.on("display_uri", ({ uri }) => {
console.log("WalletConnect URI:", uri);
});Available Events:
session_proposal- New session proposal receivedsession_update- Session updated or approvedsession_delete- Session disconnectedsession_request- Request received from walletdisplay_uri- URI generated and ready to display
off<T>(event: WalletConnectEvent, handler: EventHandler<T>): void
Remove event listener.
const handler = (data) => console.log(data);
client.on("session_update", handler);
client.off("session_update", handler);destroy(): Promise<void>
Destroy the client and clean up resources.
await client.destroy();isInitialized(): boolean
Check if the client is initialized.
if (client.isInitialized()) {
// Client is ready
}getConfig(): WalletConnectConfig
Get the current configuration.
const config = client.getConfig();
console.log("Project ID:", config.projectId);🎯 Usage Examples
Vanilla JavaScript
<!DOCTYPE html>
<html>
<head>
<title>WalletConnect Example</title>
</head>
<body>
<button id="connect-btn">Connect Wallet</button>
<div id="qr-container" style="display: none;">
<h3>Scan QR Code</h3>
<img id="qr-code" />
</div>
<div id="session-info"></div>
<script type="module">
import { WalletConnectClient } from "@tomo-inc/wallet-connect-protocol";
const client = new WalletConnectClient({
projectId: "YOUR_PROJECT_ID",
metadata: {
name: "My DApp",
description: "Example DApp",
url: "https://myapp.com",
icons: ["https://myapp.com/icon.png"],
},
});
await client.initialize();
document
.getElementById("connect-btn")
.addEventListener("click", async () => {
const uri = await client.connect();
const qrCode = await client.generateQRCode(uri);
document.getElementById("qr-code").src = qrCode;
document.getElementById("qr-container").style.display = "block";
});
client.on("session_update", (session) => {
document.getElementById("session-info").innerHTML =
`Connected to: ${session.peer.metadata.name}`;
document.getElementById("qr-container").style.display = "none";
});
</script>
</body>
</html>Vue 3
<template>
<div>
<button @click="handleConnect">Connect Wallet</button>
<div v-if="qrCode">
<h3>Scan QR Code</h3>
<img :src="qrCode" />
</div>
<div v-if="sessions.length > 0">
<h3>Connected Sessions</h3>
<ul>
<li v-for="session in sessions" :key="session.topic">
{{ session.peer.metadata.name }}
<button @click="disconnect(session.topic)">Disconnect</button>
</li>
</ul>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, onUnmounted } from "vue";
import { WalletConnectClient } from "@tomo-inc/wallet-connect-protocol";
const client = new WalletConnectClient({
projectId: "YOUR_PROJECT_ID",
metadata: {
name: "My Vue DApp",
description: "Example Vue DApp",
url: "https://myapp.com",
icons: ["https://myapp.com/icon.png"],
},
});
const qrCode = ref<string | null>(null);
const sessions = ref<any[]>([]);
onMounted(async () => {
await client.initialize();
client.on("session_update", (session) => {
sessions.value = client.getActiveSessions();
qrCode.value = null;
});
});
const handleConnect = async () => {
const uri = await client.connect();
qrCode.value = await client.generateQRCode(uri);
};
const disconnect = async (topic: string) => {
await client.disconnectSession(topic);
sessions.value = client.getActiveSessions();
};
onUnmounted(async () => {
await client.destroy();
});
</script>Angular
import { Component, OnInit, OnDestroy } from "@angular/core";
import {
WalletConnectClient,
SessionInfo,
} from "@tomo-inc/wallet-connect-protocol";
@Component({
selector: "app-wallet-connect",
template: `
<button (click)="handleConnect()">Connect Wallet</button>
<div *ngIf="qrCode">
<h3>Scan QR Code</h3>
<img [src]="qrCode" />
</div>
<div *ngIf="sessions.length > 0">
<h3>Connected Sessions</h3>
<ul>
<li *ngFor="let session of sessions">
{{ session.peer.metadata.name }}
<button (click)="disconnect(session.topic)">Disconnect</button>
</li>
</ul>
</div>
`,
})
export class WalletConnectComponent implements OnInit, OnDestroy {
private client: WalletConnectClient;
qrCode: string | null = null;
sessions: SessionInfo[] = [];
constructor() {
this.client = new WalletConnectClient({
projectId: "YOUR_PROJECT_ID",
metadata: {
name: "My Angular DApp",
description: "Example Angular DApp",
url: "https://myapp.com",
icons: ["https://myapp.com/icon.png"],
},
});
}
async ngOnInit() {
await this.client.initialize();
this.client.on("session_update", () => {
this.sessions = this.client.getActiveSessions();
this.qrCode = null;
});
}
async handleConnect() {
const uri = await this.client.connect();
this.qrCode = await this.client.generateQRCode(uri);
}
async disconnect(topic: string) {
await this.client.disconnectSession(topic);
this.sessions = this.client.getActiveSessions();
}
async ngOnDestroy() {
await this.client.destroy();
}
}Svelte
<script lang="ts">
import { onMount, onDestroy } from 'svelte';
import { WalletConnectClient } from '@tomo-inc/wallet-connect-protocol';
let client: WalletConnectClient;
let qrCode: string | null = null;
let sessions: any[] = [];
onMount(async () => {
client = new WalletConnectClient({
projectId: 'YOUR_PROJECT_ID',
metadata: {
name: 'My Svelte DApp',
description: 'Example Svelte DApp',
url: 'https://myapp.com',
icons: ['https://myapp.com/icon.png'],
},
});
await client.initialize();
client.on('session_update', () => {
sessions = client.getActiveSessions();
qrCode = null;
});
});
async function handleConnect() {
const uri = await client.connect();
qrCode = await client.generateQRCode(uri);
}
async function disconnect(topic: string) {
await client.disconnectSession(topic);
sessions = client.getActiveSessions();
}
onDestroy(async () => {
if (client) {
await client.destroy();
}
});
</script>
<button on:click={handleConnect}>Connect Wallet</button>
{#if qrCode}
<div>
<h3>Scan QR Code</h3>
<img src={qrCode} alt="WalletConnect QR Code" />
</div>
{/if}
{#if sessions.length > 0}
<div>
<h3>Connected Sessions</h3>
<ul>
{#each sessions as session}
<li>
{session.peer.metadata.name}
<button on:click={() => disconnect(session.topic)}>Disconnect</button>
</li>
{/each}
</ul>
</div>
{/if}React
import { useEffect, useState } from "react";
import {
WalletConnectClient,
SessionInfo,
} from "@tomo-inc/wallet-connect-protocol";
function WalletConnect() {
const [client] = useState(
() =>
new WalletConnectClient({
projectId: "YOUR_PROJECT_ID",
metadata: {
name: "My React DApp",
description: "Example React DApp",
url: "https://myapp.com",
icons: ["https://myapp.com/icon.png"],
},
}),
);
const [qrCode, setQrCode] = useState<string | null>(null);
const [sessions, setSessions] = useState<SessionInfo[]>([]);
useEffect(() => {
client.initialize();
const handleSessionUpdate = () => {
setSessions(client.getActiveSessions());
setQrCode(null);
};
client.on("session_update", handleSessionUpdate);
return () => {
client.off("session_update", handleSessionUpdate);
client.destroy();
};
}, [client]);
const handleConnect = async () => {
const uri = await client.connect();
const qr = await client.generateQRCode(uri);
setQrCode(qr);
};
const disconnect = async (topic: string) => {
await client.disconnectSession(topic);
setSessions(client.getActiveSessions());
};
return (
<div>
<button onClick={handleConnect}>Connect Wallet</button>
{qrCode && (
<div>
<h3>Scan QR Code</h3>
<img src={qrCode} alt="WalletConnect QR Code" />
</div>
)}
{sessions.length > 0 && (
<div>
<h3>Connected Sessions</h3>
<ul>
{sessions.map((session) => (
<li key={session.topic}>
{session.peer.metadata.name}
<button onClick={() => disconnect(session.topic)}>
Disconnect
</button>
</li>
))}
</ul>
</div>
)}
</div>
);
}🔧 Advanced Usage
Multi-Chain Support
import {
createMultiChainNamespaces,
EVM_CHAINS,
SOLANA_CHAINS,
} from "@tomo-inc/wallet-connect-protocol";
// Create multi-chain namespaces configuration
const namespaces = createMultiChainNamespaces({
evm: {
chains: [EVM_CHAINS.ethereum, EVM_CHAINS.polygon],
methods: ["eth_sendTransaction", "personal_sign"],
},
solana: {
chains: [SOLANA_CHAINS.mainnet],
methods: ["solana_signTransaction", "solana_signMessage"],
},
});
const uri = await client.connect({
requiredNamespaces: namespaces,
});SIWE (Sign-In with Ethereum)
import { SiweAuth, createSiweMessage } from "@tomo-inc/wallet-connect-protocol";
const siwe = new SiweAuth({
domain: "myapp.com",
uri: "https://myapp.com",
statement: "Sign in to My DApp",
});
// Create SIWE message
const message = createSiweMessage({
domain: "myapp.com",
address: "0x...",
chainId: 1,
uri: "https://myapp.com",
statement: "Sign in to My DApp",
});
// Request signature from wallet
const signature = await client.sendRequest({
topic: sessionTopic,
chainId: "eip155:1",
request: {
method: "personal_sign",
params: [message, address],
},
});
// Verify signature
const isValid = await siwe.verify(message, signature);Custom QR Code Styling
const qrCode = await client.generateQRCode(uri, {
width: 400,
margin: 8,
errorCorrectionLevel: "H",
color: {
dark: "#1a1a1a",
light: "#f0f0f0",
},
});Wallet Discovery
import {
getAllWallets,
getRecommendedWallets,
searchWallets,
} from "@tomo-inc/wallet-connect-protocol";
// Get all available wallets
const allWallets = await getAllWallets();
// Get recommended wallets
const recommended = await getRecommendedWallets();
// Search for specific wallets
const results = await searchWallets("metamask");🌐 Utility Functions
The SDK provides various utility functions:
import {
formatAddress, // Format address: 0x1234...5678
isValidWalletConnectUri, // Validate WC URI
parseWalletConnectUri, // Parse WC URI
extractChainIdFromAccount, // Extract chain ID from account
extractAddressFromAccount, // Extract address from account
isSessionExpired, // Check if session expired
getSessionTimeRemaining, // Get remaining session time
generateDeepLink, // Generate wallet deep link
isMobile, // Check if on mobile device
copyToClipboard, // Copy text to clipboard
} from "@tomo-inc/wallet-connect-protocol";
// Example usage
const shortAddress = formatAddress("0x1234567890abcdef");
// Returns: '0x1234...cdef'
const isValid = isValidWalletConnectUri(uri);
// Returns: true or false
const deepLink = generateDeepLink("metamask", uri);
// Returns: 'metamask://wc?uri=...'❓ FAQ
How to get Project ID?
Visit WalletConnect Cloud, sign up and create a new project to get a free Project ID.
QR Code not showing?
Ensure dependencies are correctly installed and the URI is a valid WalletConnect URI (starts with wc:).
How to handle connection errors?
Use try-catch to handle errors:
try {
await client.connect();
} catch (error) {
console.error("Connection failed:", error);
// Handle error, e.g., show error message
}Which browsers are supported?
All modern browsers (Chrome, Firefox, Safari, Edge) are supported. ES2022+ support required.
Can I use this with React?
Yes! This SDK is framework-agnostic and works perfectly with React. See the React example above.
How do I support multiple chains?
Use the createMultiChainNamespaces utility to configure support for multiple blockchain networks.
🤝 Contributing
Contributions are welcome! Please check our Contributing Guide for more information.
📄 License
MIT © Tomo Inc.
🔗 Related Links
📞 Support
For questions or suggestions, please submit an issue.
