Important: Keplr Compatibility
Hana Wallet emulates the Keplr Wallet API.
If your dApp already integrates with Keplr Wallet, it will work with Hana Wallet with minimal or no changes.
Use window.keplr
to interact with Hana Wallet. Hana injects itself as keplr
in the browser window object and implements the Keplr interface.
Supported Chains
Network | Mainnet | Testnet |
Cosmos Hub | cosmoshub-4 | theta-testnet-001 |
Osmosis | osmosis-1 | osmo-test-5 |
Neutron | neutron-1 | pion-1 |
Archway | archway-1 | constantine-3 |
Injective | injective-1 | injective-888 |
To support other Cosmos chains, use the experimentalSuggestChain
method.
Prerequisites
A web application (React or other JavaScript framework)
Installed dependency:
@cosmjs/stargate
Basic Integration
1. Detecting Hana Wallet
const checkForHanaWallet = async () => {
if (window.keplr) {
console.log('Wallet detected');
return true;
} else {
console.log('No wallet detected');
return false;
}
};
2. Connecting to a Chain
const connectToChain = async (chainId) => {
if (!window.keplr) {
alert("Please install Hana Wallet");
return;
}
try {
await window.keplr.enable(chainId);
const offlineSigner = window.keplr.getOfflineSigner(chainId);
const accounts = await offlineSigner.getAccounts();
const address = accounts[0].address;
console.log(`Connected to ${chainId} with address: ${address}`);
return { offlineSigner, address };
} catch (err) {
console.error(`Failed to connect: ${err.message}`);
}
};
3. Getting the User's Address
const getUserAddress = async (chainId) => {
try {
await window.keplr.enable(chainId);
const key = await window.keplr.getKey(chainId);
return key.bech32Address;
} catch (err) {
console.error(`Error getting address: ${err.message}`);
throw err;
}
};
4. Signing an Arbitrary Message
const signArbitraryMessage = async (chainId, address, message) => {
try {
await window.keplr.enable(chainId);
const signature = await window.keplr.signArbitrary(chainId, address, message);
console.log("Signature:", signature);
return signature;
} catch (err) {
console.error(`Signing failed: ${err.message}`);
throw err;
}
};
5. Sending Tokens
const sendTokens = async (chainId, recipientAddress, amount) => {
try {
await window.keplr.enable(chainId);
const offlineSigner = window.keplr.getOfflineSigner(chainId);
const accounts = await offlineSigner.getAccounts();
const senderAddress = accounts[0].address;
let rpcEndpoint;
if (chainId === "cosmoshub-4") {
rpcEndpoint = "https://rpc.cosmos.network";
} else if (chainId === "osmosis-1") {
rpcEndpoint = "https://rpc.osmosis.zone";
} else if (chainId === "neutron-1") {
rpcEndpoint = "https://rpc-neutron.keplr.app";
} else if (chainId === "archway-1") {
rpcEndpoint = "https://rpc.mainnet.archway.io";
} else if (chainId === "injective-1") {
rpcEndpoint = "https://rpc-injective.keplr.app";
}
const client = await SigningStargateClient.connectWithSigner(rpcEndpoint, offlineSigner);
let denom = "uatom";
if (chainId.includes("osmosis")) {
denom = "uosmo";
} else if (chainId.includes("neutron")) {
denom = "untrn";
} else if (chainId.includes("archway")) {
denom = "aarch";
} else if (chainId.includes("injective")) {
denom = "inj";
}
const amountToSend = {
denom: denom,
amount: amount,
};
const fee = {
amount: [{ denom: denom, amount: "5000" }],
gas: "200000",
};
const result = await client.sendTokens(senderAddress, recipientAddress, [amountToSend], fee, "");
console.log("Transaction hash:", result.transactionHash);
return result;
} catch (err) {
console.error(`Token transfer failed: ${err.message}`);
throw err;
}
};
Adding Custom Chains
const suggestChain = async (chainInfo) => {
try {
await window.keplr.experimentalSuggestChain(chainInfo);
console.log(`Chain ${chainInfo.chainId} added successfully`);
return true;
} catch (err) {
console.error(`Failed to add chain: ${err.message}`);
throw err;
}
};
Example chain info for Osmosis Testnet:
const osmosisTestnet = {
chainId: "osmo-test-5",
chainName: "Osmosis Testnet",
rpc: "https://rpc.testnet.osmosis.zone",
rest: "https://lcd.testnet.osmosis.zone",
stakeCurrency: {
coinDenom: "OSMO",
coinMinimalDenom: "uosmo",
coinDecimals: 6
},
bip44: {
coinType: 118
},
bech32Config: {
bech32PrefixAccAddr: "osmo",
bech32PrefixAccPub: "osmopub",
bech32PrefixValAddr: "osmovaloper",
bech32PrefixValPub: "osmovaloperpub",
bech32PrefixConsAddr: "osmovalcons",
bech32PrefixConsPub: "osmovalconspub"
},
currencies: [
{
coinDenom: "OSMO",
coinMinimalDenom: "uosmo",
coinDecimals: 6
}
],
feeCurrencies: [
{
coinDenom: "OSMO",
coinMinimalDenom: "uosmo",
coinDecimals: 6,
gasPriceStep: {
low: 0.01,
average: 0.025,
high: 0.04
}
}
],
coinType: 118,
beta: true
};
Technical Details
Hana Wallet implements the following Keplr interfaces:
getOfflineSigner
: Returns a signer for Amino and Direct signinggetOfflineSignerOnlyAmino
: For Ledger compatibilitygetOfflineSignerAuto
: Automatically chooses the right modesignAmino
: Sign a transaction in Amino formatsignDirect
: Sign in Direct signing formatsignArbitrary
: Sign arbitrary messages (ADR-36)experimentalSuggestChain
: Suggest new chains
Tips for dApp Developers
Always use
enable()
first before accessing account infoHandle rejections and errors to give users clear feedback
Support multiple chains by dynamically adjusting denom and fees
Use reliable RPC endpoints per chain
Use
verifyArbitrary()
on the backend if you care about verifying signatures