Usage with Next.js
Before you begin, see the installation guide for details.
Setup
Step 1: Create Next.js Project
# Create a new Next.js project
npx create-next-app@latest my-hedera-app
cd my-hedera-app
Step 2: Create WalletProvider
Create providers/WalletProvider.tsx
:
'use client';
import { HashinalsWalletConnectSDK } from '@hashgraphonline/hashinal-wc';
import { createContext, useContext, useEffect, useState, useCallback, ReactNode } from 'react';
import { PrivateKey } from '@hashgraph/hedera-wallet-connect';
interface WalletContextType {
connect: () => Promise<void>;
disconnect: () => Promise<void>;
accountId: string | null;
balance: number | null;
isConnecting: boolean;
submitHCS2Message: (message: HCS2Message, topicId: string, submitKey?: string) => Promise<any>;
}
interface HCS2Message {
p: 'hcs-2';
op: 'register' | 'delete' | 'update' | 'migrate';
t_id?: string;
uid?: string;
metadata?: string;
m?: string;
}
const WalletContext = createContext<WalletContextType>({} as WalletContextType);
const PROJECT_ID = process.env.NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID!;
const APP_METADATA = {
name: 'My Hedera App',
description: 'Next.js app using Hashinal WalletConnect',
url: typeof window !== 'undefined' ? window.location.origin : '',
icons: ['https://your-app-icon.com/icon.png']
};
export function WalletProvider({ children }: { children: ReactNode }) {
const [sdk, setSdk] = useState<HashinalsWalletConnectSDK | null>(null);
const [accountId, setAccountId] = useState<string | null>(null);
const [balance, setBalance] = useState<number | null>(null);
const [isConnecting, setIsConnecting] = useState(false);
useEffect(() => {
const initSDK = async () => {
const instance = HashinalsWalletConnectSDK.getInstance();
setSdk(instance);
try {
const existingAccount = await instance.initAccount(
PROJECT_ID,
APP_METADATA
);
if (existingAccount) {
setAccountId(existingAccount.accountId);
setBalance(existingAccount.balance);
}
} catch (error) {
console.error('Failed to init wallet:', error);
}
};
initSDK();
}, []);
const connect = async () => {
if (!sdk) return;
setIsConnecting(true);
try {
const { accountId, balance } = await sdk.connectWallet(
PROJECT_ID,
APP_METADATA
);
setAccountId(accountId);
setBalance(balance);
} catch (error) {
console.error('Failed to connect wallet:', error);
throw error;
} finally {
setIsConnecting(false);
}
};
const disconnect = async () => {
if (!sdk) return;
try {
await sdk.disconnectWallet();
setAccountId(null);
setBalance(null);
} catch (error) {
console.error('Failed to disconnect wallet:', error);
throw error;
}
};
const submitHCS2Message = useCallback(async (
message: HCS2Message,
topicId: string,
submitKey?: string
) => {
try {
const sdk = HashinalsWalletConnectSDK.getInstance();
// Validate message format
if (message.m && message.m.length > 500) {
throw new Error("Memo must not exceed 500 characters");
}
if (!message.p || message.p !== 'hcs-2') {
throw new Error("Invalid protocol. Must be 'hcs-2'");
}
// Convert message to string
const messageString = JSON.stringify(message);
// If a submit key is provided, convert it to a PrivateKey object
const privateKey = submitKey ? PrivateKey.fromString(submitKey) : undefined;
const receipt = await sdk.submitMessageToTopic(topicId, messageString, privateKey);
console.log("HCS-2 message submitted successfully!");
console.log("Transaction ID:", receipt.transactionId.toString());
return receipt;
} catch (error) {
console.error("Error submitting HCS-2 message:", error);
throw error;
}
}, []);
return (
<WalletContext.Provider
value={{
connect,
disconnect,
accountId,
balance,
isConnecting,
submitHCS2Message,
}}
>
{children}
</WalletContext.Provider>
);
}
export const useWallet = () => useContext(WalletContext);
Step 3: Update Root Layout
Update app/layout.tsx
:
import { WalletProvider } from '@/providers/WalletProvider'
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<body>
<WalletProvider>
{children}
</WalletProvider>
</body>
</html>
)
}
Step 4: Create Wallet Component
Create components/WalletButton.tsx
:
'use client';
import { useWallet } from '@/providers/WalletProvider';
export function WalletButton() {
const { connect, disconnect, accountId, balance, isConnecting } = useWallet();
if (isConnecting) {
return <button disabled>Connecting...</button>;
}
if (accountId) {
return (
<div>
<div>Account: {accountId}</div>
<div>Balance: {balance} HBAR</div>
<button onClick={disconnect}>Disconnect</button>
</div>
);
}
return <button onClick={connect}>Connect Wallet</button>;
}
Step 5: Use in Page
Update app/page.tsx
:
import { WalletButton } from '@/components/WalletButton';
export default function Home() {
return (
<main className="min-h-screen p-24">
<h1>My Hedera App</h1>
<WalletButton />
</main>
);
}
Environment Setup
Create .env.local
:
NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID=your_project_id_here
Usage Examples
Send Transaction
import { useWallet } from '@/providers/WalletProvider';
export function SendButton() {
const { sdk, accountId } = useWallet();
const sendTransaction = async () => {
if (!sdk || !accountId) return;
try {
const result = await sdk.transferHBAR({
recipientId: "0.0.123456",
amount: 1, // 1 HBAR
});
console.log('Transaction success:', result);
} catch (error) {
console.error('Transaction failed:', error);
}
};
return (
<button onClick={sendTransaction} disabled={!accountId}>
Send 1 HBAR
</button>
);
}
Register Topic Example
Here's an example of how to use the submitHCS2Message
function to register a new topic:
// RegisterTopicComponent.tsx
import { useWallet } from './WalletProvider';
export function RegisterTopicComponent() {
const { submitHCS2Message } = useWallet();
const handleRegisterTopic = async () => {
try {
const topicId = "0.0.12345"; // Your HCS topic ID
// Example of registering a new topic
const registerMessage: HCS2Message = {
p: "hcs-2",
op: "register",
t_id: "0.0.456789",
metadata: "hcs://1/0.0.456789",
m: "register new topic"
};
const receipt = await submitHCS2Message(registerMessage, topicId);
console.log("Topic registered successfully:", receipt);
} catch (error) {
console.error("Failed to register topic:", error);
}
};
return (
<button onClick={handleRegisterTopic}>
Register Topic
</button>
);
}
Update Topic Example
Here's an example of updating an existing topic:
// UpdateTopicComponent.tsx
import { useWallet } from './WalletProvider';
export function UpdateTopicComponent() {
const { submitHCS2Message } = useWallet();
const handleUpdateTopic = async () => {
try {
const topicId = "0.0.12345"; // Your HCS topic ID
// Example of updating an existing topic
const updateMessage: HCS2Message = {
p: "hcs-2",
op: "update",
uid: "60", // sequence number of the message to update
t_id: "0.0.123456",
metadata: "hcs://1/0.0.456789",
m: "update sequence number 60 to a new topic id and metadata"
};
const receipt = await submitHCS2Message(updateMessage, topicId);
console.log("Topic updated successfully:", receipt);
} catch (error) {
console.error("Failed to update topic:", error);
}
};
return (
<button onClick={handleUpdateTopic}>
Update Topic
</button>
);
}
Remember to replace your_project_id_here
with your actual WalletConnect project ID from the WalletConnect Dashboard!