# Integrating Keplr Wallet and CosmWasm Smart Contracts: A Comprehensive Guide
## Table of Contents
1. [Prerequisites](#prerequisites)
2. [Project Setup](#project-setup)
3. [Keplr Wallet Integration](#keplr-wallet-integration)
4. [CosmWasm Contract Integration](#cosmwasm-contract-integration)
5. [Building the UI Components](#building-the-ui-components)
6. [Testing and Deployment](#testing-and-deployment)
## Prerequisites
Before starting, ensure you have the following installed:
- Node.js (v14 or higher)
- npm or yarn
- A CosmWasm-compatible blockchain network (e.g., Osmosis, Juno)
- Keplr wallet browser extension
## Project Setup
First, create a new React project using Create React App:
```bash
npx create-react-app keplr-cosmwasm-demo
cd keplr-cosmwasm-demo
npm install @keplr-wallet/types @cosmjs/cosmwasm-stargate @cosmjs/proto-signing
```
Create the following project structure:
```
src/
├── components/
│ ├── WalletConnect.tsx
│ ├── ContractInteraction.tsx
│ └── TokenBalance.tsx
├── hooks/
│ ├── useKeplr.ts
│ └── useContract.ts
├── config/
│ └── chain.ts
└── App.tsx
```
## Keplr Wallet Integration
### 1. Chain Configuration
Create `src/config/chain.ts`:
```typescript
export const chainConfig = {
chainId: "juno-1",
chainName: "Juno",
rpc: "https://rpc-juno.itastakers.com",
rest: "https://lcd-juno.itastakers.com",
bip44: {
coinType: 118,
},
bech32Config: {
bech32PrefixAccAddr: "juno",
bech32PrefixAccPub: "junopub",
bech32PrefixValAddr: "junovaloper",
bech32PrefixValPub: "junovaloperpub",
bech32PrefixConsAddr: "junovalcons",
bech32PrefixConsPub: "junovalconspub",
},
currencies: [
{
coinDenom: "JUNO",
coinMinimalDenom: "ujuno",
coinDecimals: 6,
},
],
feeCurrencies: [
{
coinDenom: "JUNO",
coinMinimalDenom: "ujuno",
coinDecimals: 6,
},
],
stakeCurrency: {
coinDenom: "JUNO",
coinMinimalDenom: "ujuno",
coinDecimals: 6,
},
gasPriceStep: {
low: 0.01,
average: 0.025,
high: 0.04,
},
};
```
### 2. Keplr Hook
Create `src/hooks/useKeplr.ts`:
```typescript
import { useState, useCallback } from 'react';
import { SigningCosmWasmClient } from '@cosmjs/cosmwasm-stargate';
import { chainConfig } from '../config/chain';
export const useKeplr = () => {
const [address, setAddress] = useState<string>('');
const [client, setClient] = useState<SigningCosmWasmClient | null>(null);
const connect = useCallback(async () => {
if (!window.keplr) {
alert('Please install Keplr extension');
return;
}
try {
// Enable the chain in Keplr
await window.keplr.enable(chainConfig.chainId);
// Get the offlineSigner for signing transactions
const offlineSigner = window.keplr.getOfflineSigner(chainConfig.chainId);
// Get user address
const accounts = await offlineSigner.getAccounts();
setAddress(accounts[0].address);
// Create signing client
const client = await SigningCosmWasmClient.connectWithSigner(
chainConfig.rpc,
offlineSigner
);
setClient(client);
} catch (error) {
console.error('Error connecting to Keplr:', error);
alert('Failed to connect to Keplr');
}
}, []);
return { address, client, connect };
};
```
## CosmWasm Contract Integration
### 1. Contract Hook
Create `src/hooks/useContract.ts`:
```typescript
import { useCallback } from 'react';
import { SigningCosmWasmClient } from '@cosmjs/cosmwasm-stargate';
export const useContract = (
client: SigningCosmWasmClient | null,
contractAddress: string
) => {
const queryContract = useCallback(
async (queryMsg: Record<string, unknown>) => {
if (!client) return null;
try {
const result = await client.queryContractSmart(contractAddress, queryMsg);
return result;
} catch (error) {
console.error('Error querying contract:', error);
return null;
}
},
[client, contractAddress]
);
const executeContract = useCallback(
async (
senderAddress: string,
executeMsg: Record<string, unknown>,
funds?: readonly Coin[]
) => {
if (!client) return null;
try {
const result = await client.execute(
senderAddress,
contractAddress,
executeMsg,
'auto',
undefined,
funds
);
return result;
} catch (error) {
console.error('Error executing contract:', error);
return null;
}
},
[client, contractAddress]
);
return { queryContract, executeContract };
};
```
## Building the UI Components
### 1. Wallet Connect Component
Create `src/components/WalletConnect.tsx`:
```typescript
import React from 'react';
import { useKeplr } from '../hooks/useKeplr';
export const WalletConnect: React.FC = () => {
const { address, connect } = useKeplr();
return (
<div>
{!address ? (
<button onClick={connect}>Connect Wallet</button>
) : (
<div>
<p>Connected Address: {address}</p>
</div>
)}
</div>
);
};
```
### 2. Contract Interaction Component
Create `src/components/ContractInteraction.tsx`:
```typescript
import React, { useState } from 'react';
import { useContract } from '../hooks/useContract';
import { useKeplr } from '../hooks/useKeplr';
interface ContractInteractionProps {
contractAddress: string;
}
export const ContractInteraction: React.FC<ContractInteractionProps> = ({
contractAddress,
}) => {
const { address, client } = useKeplr();
const { queryContract, executeContract } = useContract(client, contractAddress);
const [queryResult, setQueryResult] = useState<any>(null);
const handleQuery = async () => {
const result = await queryContract({
get_count: {} // Example query message
});
setQueryResult(result);
};
const handleExecute = async () => {
if (!address) return;
const result = await executeContract(
address,
{ increment: {} }, // Example execute message
[]
);
if (result) {
alert('Transaction successful!');
handleQuery(); // Refresh the query result
}
};
return (
<div>
<h2>Contract Interaction</h2>
<button onClick={handleQuery}>Query Contract</button>
{queryResult && (
<pre>{JSON.stringify(queryResult, null, 2)}</pre>
)}
<button onClick={handleExecute} disabled={!address}>
Execute Contract
</button>
</div>
);
};
```
### 3. App Component
Update `src/App.tsx`:
```typescript
import React from 'react';
import { WalletConnect } from './components/WalletConnect';
import { ContractInteraction } from './components/ContractInteraction';
const CONTRACT_ADDRESS = 'your-contract-address-here';
function App() {
return (
<div className="App">
<h1>Keplr + CosmWasm Demo</h1>
<WalletConnect />
<ContractInteraction contractAddress={CONTRACT_ADDRESS} />
</div>
);
}
export default App;
```
## Testing and Deployment
1. **Local Testing**
```bash
npm start
```
2. **Production Build**
```bash
npm run build
```
3. **Testing Checklist**
- Wallet connection works
- Contract queries return expected results
- Contract executions complete successfully
- Error handling works as expected
- Transaction feedback is clear to users
## Error Handling Best Practices
1. Always check for Keplr availability:
```typescript
if (!window.keplr) {
alert('Please install Keplr extension');
return;
}
```
2. Handle transaction failures:
```typescript
try {
const result = await executeContract(...);
if (!result) throw new Error('Transaction failed');
// Handle success
} catch (error) {
console.error('Transaction error:', error);
alert('Transaction failed: ' + error.message);
}
```
3. Implement loading states:
```typescript
const [isLoading, setIsLoading] = useState(false);
const handleTransaction = async () => {
setIsLoading(true);
try {
await executeContract(...);
} finally {
setIsLoading(false);
}
};
```
## Security Considerations
1. **Input Validation**: Always validate user input before sending it to the contract
2. **Gas Estimation**: Implement proper gas estimation for transactions
3. **Error Messages**: Don't expose sensitive information in error messages
4. **Transaction Confirmation**: Always wait for transaction confirmation before updating UI
5. **Network Security**: Use secure RPC endpoints and SSL/TLS connections
## Additional Features to Consider
1. **Transaction History**
2. **Balance Display**
3. **Network Selector**
4. **Gas Fee Customization**
5. **Contract Event Monitoring**
Remember to replace placeholder values like `your-contract-address-here` with actual values from your deployment.
For production applications, consider implementing additional features like:
- Loading indicators
- Better error handling
- Transaction history
- Network switching
- Gas fee customization
## Resources
- [Keplr Wallet Documentation](https://docs.keplr.app/)
- [CosmJS Documentation](https://cosmos.github.io/cosmjs/)
- [CosmWasm Documentation](https://docs.cosmwasm.com/)