Hana Wallet is a versatile, multi-chain wallet that enables seamless interactions with decentralized applications (dApps) across various blockchain networks, including Sui.
π§ This guide provides comprehensive instructions for integrating Hana Wallet into your Sui-based applications using the @mysten/dapp-kit
library.
Prerequisites
Before you begin, ensure your development environment includes:
Node.js and npm: Install Node.js (includes npm)
React: A functional React project. Create one using Create React App if needed
Integration Guide
Step 1: Install Required Packages
npm install @mysten/dapp-kit @mysten/sui
@mysten/dapp-kit
: React components and hooks for Sui dApp development@mysten/sui
: Sui TypeScript SDK for blockchain interactions
Step 2: Configure Providers
import React from 'react';
import ReactDOM from 'react-dom';
import '@mysten/dapp-kit/dist/index.css';
import { createNetworkConfig, SuiClientProvider, WalletProvider } from '@mysten/dapp-kit';
import { getFullnodeUrl } from '@mysten/sui/client';
import App from './App';
const { networkConfig } = createNetworkConfig({
localnet: { url: getFullnodeUrl('localnet') },
devnet: { url: getFullnodeUrl('devnet') },
testnet: { url: getFullnodeUrl('testnet') },
mainnet: { url: getFullnodeUrl('mainnet') },
});
ReactDOM.render(
<React.StrictMode>
<SuiClientProvider networks={networkConfig} defaultNetwork="mainnet">
<WalletProvider>
<App />
</WalletProvider>
</SuiClientProvider>
</React.StrictMode>,
document.getElementById('root')
);
Note: Set defaultNetwork
to the appropriate environment for your dApp (e.g., mainnet
or devnet
).
Step 3: Implement Wallet Connection
import React from 'react';
import { useConnectWallet, useWallets, ConnectButton } from '@mysten/dapp-kit';
function WalletConnection() {
const wallets = useWallets();
const { mutate: connect } = useConnectWallet();
return (
<div className="wallet-connection">
<h2>Connect Your Wallet</h2>
<ConnectButton />
<div className="wallet-list">
{wallets.map((wallet) => (
<button
key={wallet.name}
onClick={() => connect({ wallet })}
className="wallet-button"
>
{wallet.icon && <img src={wallet.icon} alt={`${wallet.name} icon`} />}
Connect to {wallet.name}
</button>
))}
</div>
</div>
);
}
export default WalletConnection;
Step 4: Access Wallet Information
import React from 'react';
import { useCurrentAccount } from '@mysten/dapp-kit';
function AccountInfo() {
const currentAccount = useCurrentAccount();
if (!currentAccount) {
return <p className="account-status">No wallet connected</p>;
}
return (
<div className="account-info">
<h3>Connected Account</h3>
<div className="address-container">
<span className="label">Address:</span>
<code className="address">{currentAccount.address}</code>
</div>
{currentAccount.chains?.map(chain => (
<div key={chain} className="chain-info">
<span className="label">Chain:</span>
<span>{chain}</span>
</div>
))}
</div>
);
}
export default AccountInfo;
Step 5: Sign Personal Messages
import React, { useState } from 'react';
import { useSignPersonalMessage, useCurrentAccount } from '@mysten/dapp-kit';
function MessageSigning() {
const { mutate: signPersonalMessage, isPending } = useSignPersonalMessage();
const currentAccount = useCurrentAccount();
const [message, setMessage] = useState('Hello from my Sui dApp!');
const [signature, setSignature] = useState('');
const [error, setError] = useState('');
const handleSignMessage = () => {
if (!currentAccount) {
setError('Please connect your wallet first');
return;
}
setError('');
signPersonalMessage(
{ message: new TextEncoder().encode(message) },
{
onSuccess: (result) => {
setSignature(result.signature);
console.log('Full result:', result);
},
onError: (err) => {
setError(`Signing failed: ${err.message}`);
console.error('Signing error:', err);
},
}
);
};
return (
<div className="message-signing">
<h3>Sign Message</h3>
<textarea
id="message"
value={message}
onChange={(e) => setMessage(e.target.value)}
rows={3}
className="input-field"
/>
<button
onClick={handleSignMessage}
disabled={isPending || !currentAccount}
className="sign-button"
>
{isPending ? 'Signing...' : 'Sign Message'}
</button>
{error && <div className="error-message">{error}</div>}
{signature && (
<div className="signature-result">
<h4>Signature:</h4>
<code>{signature}</code>
</div>
)}
</div>
);
}
export default MessageSigning;
Step 6: Execute Transactions
import React, { useState } from 'react';
import { useSignAndExecuteTransaction, useCurrentAccount } from '@mysten/dapp-kit';
import { TransactionBlock } from '@mysten/sui/transactions';
function TransactionExecution() {
const { mutate: signAndExecuteTransaction, isPending } = useSignAndExecuteTransaction();
const currentAccount = useCurrentAccount();
const [txResult, setTxResult] = useState(null);
const [error, setError] = useState('');
const handleTransaction = async () => {
if (!currentAccount) {
setError('Please connect your wallet first');
return;
}
try {
const tx = new TransactionBlock();
const [coin] = tx.splitCoins(tx.gas, [tx.pure(1000000)]);
tx.transferObjects([coin], tx.pure(currentAccount.address));
tx.setGasBudget(10000000);
setError('');
signAndExecuteTransaction(
{ transactionBlock: tx },
{
onSuccess: (result) => {
setTxResult(result);
},
onError: (err) => {
setError(`Transaction failed: ${err.message}`);
},
}
);
} catch (err) {
setError(`Error creating transaction: ${err.message}`);
}
};
return (
<div className="transaction-execution">
<h3>Execute Transaction</h3>
<button
onClick={handleTransaction}
disabled={isPending || !currentAccount}
className="transaction-button"
>
{isPending ? 'Processing...' : 'Send Test Transaction'}
</button>
{error && <div className="error-message">{error}</div>}
{txResult && (
<div className="transaction-result">
<p><strong>Digest:</strong> <code>{txResult.digest}</code></p>
<p><strong>Status:</strong> <span>{txResult.effects?.status?.status}</span></p>
</div>
)}
</div>
);
}
export default TransactionExecution;
Step 7: Manage Wallet Disconnection
import React from 'react';
import { useDisconnectWallet, useCurrentAccount } from '@mysten/dapp-kit';
function WalletDisconnection() {
const { mutate: disconnect, isPending } = useDisconnectWallet();
const currentAccount = useCurrentAccount();
if (!currentAccount) return null;
return (
<div className="wallet-disconnection">
<button
onClick={() => disconnect()}
disabled={isPending}
className="disconnect-button"
>
{isPending ? 'Disconnecting...' : 'Disconnect Wallet'}
</button>
</div>
);
}
export default WalletDisconnection;
Step 8: Complete Example App
import React from 'react';
import WalletConnection from './components/WalletConnection';
import AccountInfo from './components/AccountInfo';
import MessageSigning from './components/MessageSigning';
import TransactionExecution from './components/TransactionExecution';
import WalletDisconnection from './components/WalletDisconnection';
import { useCurrentAccount } from '@mysten/dapp-kit';
function App() {
const currentAccount = useCurrentAccount();
return (
<div className="app-container">
<h1>Hana Wallet Integration Demo</h1>
<WalletConnection />
{currentAccount ? (
<>
<AccountInfo />
<MessageSigning />
<TransactionExecution />
<WalletDisconnection />
</>
) : (
<p>Connect your Hana Wallet to begin</p>
)}
</div>
);
}
export default App;
Advanced Features
Custom Network Configuration
const { networkConfig } = createNetworkConfig({
mainnet: {
url: "https://your-custom-rpc-endpoint.com",
faucetUrl: "https://faucet.your-service.com",
description: "Production Network"
},
});
Error Handling Example
try {
// wallet operation
} catch (error) {
if (error.code === 4001) {
setErrorMessage("Transaction was rejected.");
} else if (error.message.includes("insufficient funds")) {
setErrorMessage("Not enough SUI in your wallet.");
} else {
setErrorMessage(`An error occurred: ${error.message}`);
console.error(error);
}
}
Testing & Debugging
Use
defaultNetwork="devnet"
for testingGet test tokens from the Sui Devnet Faucet
Ensure wallet and dApp are on the same network
Common Issues
Issue | Solution |
Wallet not connecting | Ensure Hana Wallet is installed and unlocked |
Transaction failed | Check gas budget and account balance |
Signature errors | Verify the message format |
Network mismatch | Ensure both dApp and wallet are on the same chain |