Skip to content

WalletConnect Integration ​

Connect mobile wallets and support multi-wallet experiences in your Civra applications.

Overview ​

WalletConnect is an open protocol for connecting mobile wallets to dApps. It enables users on mobile devices to interact with your Web3 application securely.

Why WalletConnect? ​

Benefits:

  • 📱 Mobile wallet support
  • 🔐 Secure connections (end-to-end encrypted)
  • 🌐 200+ wallet compatibility
  • 🔄 Multi-chain support
  • 📊 QR code & deep linking

Supported Wallets:

  • MetaMask Mobile
  • Trust Wallet
  • Rainbow Wallet
  • Argent
  • Coinbase Wallet
  • And 195+ more

Quick Start ​

Civra provides built-in WalletConnect support:

typescript
import { WalletConnectButton } from '@civra/web3'

export function App() {
  return (
    <div>
      <WalletConnectButton />
    </div>
  )
}

Setup ​

Installation ​

WalletConnect comes pre-installed in Civra projects. For custom setup:

bash
npm install @civra/web3

Configuration ​

typescript
// lib/walletconnect.ts
import { WalletConnectProvider } from '@civra/web3'

export const walletConnect = new WalletConnectProvider({
  projectId: process.env.NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID!,
  chains: [1, 137, 42161], // Ethereum, Polygon, Arbitrum
  showQrModal: true
})

Get Project ID ​

  1. Visit WalletConnect Cloud
  2. Create new project
  3. Copy Project ID
  4. Add to .env.local:
bash
NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID=your_project_id_here

Basic Usage ​

Connect Wallet ​

typescript
import { useWalletConnect } from '@civra/web3'

export function ConnectButton() {
  const { connect, disconnect, address, isConnected } = useWalletConnect()

  if (isConnected) {
    return (
      <div>
        <span>{address}</span>
        <button onClick={disconnect}>Disconnect</button>
      </div>
    )
  }

  return <button onClick={connect}>Connect Wallet</button>
}

Mobile-Optimized Connection ​

typescript
import { isMobile } from '@civra/web3'

export function SmartConnect() {
  const { connect: connectWC } = useWalletConnect()
  const { connect: connectMM } = useMetaMask()

  const handleConnect = () => {
    if (isMobile()) {
      connectWC() // Show QR or deep link
    } else {
      connectMM() // Use browser extension
    }
  }

  return <button onClick={handleConnect}>Connect</button>
}

QR Code Modal ​

Default Modal ​

Built-in QR code modal for desktop users:

typescript
const { connect } = useWalletConnect({
  showQrModal: true // Default
})

// Click triggers QR modal
await connect()

Custom QR Modal ​

Build your own QR interface:

typescript
import { useWalletConnect, QRCodeSVG } from '@civra/web3'

export function CustomQRModal() {
  const { uri, isLoading } = useWalletConnect({
    showQrModal: false
  })

  if (isLoading) return <div>Generating QR...</div>

  return (
    <div className="modal">
      <h2>Scan with Your Wallet</h2>
      <QRCodeSVG value={uri} size={256} />
      <p>Open your mobile wallet and scan</p>
    </div>
  )
}

Deep Linking ​

Automatically open wallet app on mobile:

typescript
import { useWalletConnect } from '@civra/web3'

const { connect } = useWalletConnect({
  deepLink: {
    enabled: true,
    wallets: ['metamask', 'trust', 'rainbow']
  }
})

// On mobile, opens wallet app directly
await connect()
typescript
const { generateDeepLink } = useWalletConnect()

const deepLink = generateDeepLink({
  wallet: 'trust', // or 'metamask', 'rainbow', etc.
  uri: walletConnectURI
})

// Open in wallet
window.location.href = deepLink
typescript
export function WalletButtons() {
  const { connectWithWallet } = useWalletConnect()

  return (
    <div>
      <button onClick={() => connectWithWallet('metamask')}>
        MetaMask
      </button>
      <button onClick={() => connectWithWallet('trust')}>
        Trust Wallet
      </button>
      <button onClick={() => connectWithWallet('rainbow')}>
        Rainbow
      </button>
    </div>
  )
}

Multi-Chain Support ​

Configure Chains ​

typescript
import { mainnet, polygon, arbitrum, optimism } from '@civra/chains'

const { connect } = useWalletConnect({
  chains: [mainnet, polygon, arbitrum, optimism],
  defaultChain: polygon
})

Switch Chains ​

typescript
import { useSwitchChain } from '@civra/web3'

export function ChainSwitcher() {
  const { switchChain, currentChain } = useSwitchChain()

  return (
    <div>
      <p>Current: {currentChain.name}</p>
      <button onClick={() => switchChain(1)}>
        Ethereum
      </button>
      <button onClick={() => switchChain(137)}>
        Polygon
      </button>
    </div>
  )
}

Session Management ​

Session Persistence ​

WalletConnect sessions persist across browser refreshes:

typescript
import { useEffect } from 'react'
import { useWalletConnect } from '@civra/web3'

export function App() {
  const { autoConnect } = useWalletConnect()

  useEffect(() => {
    // Auto-reconnect on page load
    autoConnect()
  }, [])

  return <YourApp />
}

Session Events ​

typescript
const { on, off } = useWalletConnect()

useEffect(() => {
  // Listen to session events
  const handleConnect = () => console.log('Connected')
  const handleDisconnect = () => console.log('Disconnected')
  const handleChainChanged = (chainId) => console.log('Chain:', chainId)
  const handleAccountsChanged = (accounts) => console.log('Account:', accounts[0])

  on('connect', handleConnect)
  on('disconnect', handleDisconnect)
  on('chainChanged', handleChainChanged)
  on('accountsChanged', handleAccountsChanged)

  return () => {
    off('connect', handleConnect)
    off('disconnect', handleDisconnect)
    off('chainChanged', handleChainChanged)
    off('accountsChanged', handleAccountsChanged)
  }
}, [])

Custom Session Timeout ​

typescript
const { connect } = useWalletConnect({
  sessionTimeout: 7 * 24 * 60 * 60 * 1000 // 7 days
})

Transactions ​

Send Transaction ​

typescript
import { useSendTransaction } from '@civra/web3'

export function SendButton() {
  const { sendTransaction } = useSendTransaction()

  const handleSend = async () => {
    const tx = await sendTransaction({
      to: '0x...',
      value: parseEther('0.1')
    })

    console.log('Transaction hash:', tx.hash)
    await tx.wait()
    console.log('Confirmed!')
  }

  return <button onClick={handleSend}>Send 0.1 ETH</button>
}

Sign Message ​

typescript
import { useSignMessage } from '@civra/web3'

export function SignButton() {
  const { signMessage } = useSignMessage()

  const handleSign = async () => {
    const signature = await signMessage('Hello from Civra!')
    console.log('Signature:', signature)
  }

  return <button onClick={handleSign}>Sign Message</button>
}

Contract Interaction ​

typescript
import { useContract } from '@civra/web3'

const contract = useContract(CONTRACT_ADDRESS, ABI)

// Read
const balance = await contract.read.balanceOf(address)

// Write
const tx = await contract.write.transfer(to, amount)
await tx.wait()

Advanced Features ​

Custom RPC URLs ​

typescript
const { connect } = useWalletConnect({
  rpcMap: {
    1: 'https://eth-mainnet.alchemyapi.io/v2/YOUR-KEY',
    137: 'https://polygon-mainnet.g.alchemy.com/v2/YOUR-KEY'
  }
})

Network Metadata ​

typescript
const { connect } = useWalletConnect({
  chains: [
    {
      chainId: 1,
      name: 'Ethereum',
      currency: 'ETH',
      rpcUrl: 'https://...',
      explorerUrl: 'https://etherscan.io'
    }
  ]
})

Custom Theme ​

typescript
const { connect } = useWalletConnect({
  theme: {
    primaryColor: '#FF6B6B',
    background: '#1a1a1a',
    fontFamily: 'Inter, sans-serif'
  }
})

Error Handling ​

Common Errors ​

typescript
import { useWalletConnect } from '@civra/web3'

export function SafeConnect() {
  const { connect } = useWalletConnect()

  const handleConnect = async () => {
    try {
      await connect()
    } catch (error) {
      if (error.code === 'USER_REJECTED') {
        console.log('User rejected connection')
      } else if (error.code === 'SESSION_EXPIRED') {
        console.log('Session expired, please reconnect')
      } else if (error.code === 'NETWORK_ERROR') {
        console.log('Network error, please try again')
      } else {
        console.error('Connection failed:', error)
      }
    }
  }

  return <button onClick={handleConnect}>Connect</button>
}

Error Codes ​

CodeMeaningSolution
USER_REJECTEDUser cancelledLet user retry
SESSION_EXPIREDSession timed outReconnect
NETWORK_ERRORConnection issueCheck internet
CHAIN_NOT_SUPPORTEDWrong networkAdd chain support
WALLET_NOT_READYWallet loadingWait and retry

Mobile Optimization ​

Detect Mobile ​

typescript
import { isMobile, getMobileOS } from '@civra/web3'

if (isMobile()) {
  const os = getMobileOS() // 'ios' | 'android'

  if (os === 'ios') {
    // iOS-specific logic
  } else {
    // Android-specific logic
  }
}

Responsive Connection ​

typescript
export function ResponsiveConnect() {
  const { connect: wcConnect } = useWalletConnect()
  const { connect: mmConnect } = useMetaMask()

  if (isMobile()) {
    return (
      <div>
        <button onClick={wcConnect}>
          Connect Mobile Wallet
        </button>
      </div>
    )
  }

  return (
    <div>
      <button onClick={mmConnect}>
        Connect MetaMask
      </button>
      <button onClick={wcConnect}>
        Show QR Code
      </button>
    </div>
  )
}
typescript
const { connect } = useWalletConnect({
  deepLink: {
    // Auto-select based on installed apps
    autoDetect: true,

    // Fallback if no app detected
    fallbackUrl: 'https://apps.apple.com/app/trust-wallet',

    // Custom success redirect
    redirect: '/dashboard'
  }
})

Testing ​

Mock WalletConnect ​

typescript
import { mockWalletConnect } from '@civra/web3/testing'

test('user can connect via WalletConnect', async () => {
  const { connect } = mockWalletConnect({
    address: '0x123...',
    chainId: 1
  })

  await connect()
  expect(getAddress()).toBe('0x123...')
})

Test Networks ​

typescript
// Use testnets for development
const { connect } = useWalletConnect({
  chains: [sepolia, mumbai], // Testnets
  // chains: [mainnet, polygon] // Production
})

Best Practices ​

1. Always Provide Fallbacks ​

typescript
// Good: Multiple options
<div>
  <MetaMaskButton />
  <WalletConnectButton />
  <CoinbaseButton />
</div>

// Bad: Single option only
<div>
  <WalletConnectButton />
</div>

2. Handle Session Expiry ​

typescript
useEffect(() => {
  const checkSession = setInterval(() => {
    if (!isConnected && hasSession) {
      autoConnect() // Try to reconnect
    }
  }, 5000)

  return () => clearInterval(checkSession)
}, [])

3. Show Connection Status ​

typescript
export function ConnectionStatus() {
  const { isConnecting, isReconnecting, error } = useWalletConnect()

  if (isConnecting) return <div>Connecting...</div>
  if (isReconnecting) return <div>Reconnecting...</div>
  if (error) return <div>Error: {error.message}</div>

  return null
}

4. Optimize for Mobile ​

  • Use deep links for better UX
  • Show QR codes on desktop only
  • Detect and suggest installed wallets
  • Provide app store links

5. Security ​

  • Always verify signatures server-side
  • Use HTTPS in production
  • Implement session timeouts
  • Validate chain IDs

Comparison: MetaMask vs WalletConnect ​

FeatureMetaMaskWalletConnect
PlatformBrowser extensionMobile-first
DevicesDesktop primarilyMobile + Desktop
ConnectionInstantQR/Deep link
WalletsMetaMask only200+ wallets
SetupExtension installApp install
UXPopupQR/App switch

Use Both:

typescript
export function UniversalConnect() {
  return (
    <div>
      {/* Desktop */}
      {!isMobile() && <MetaMaskButton />}

      {/* Mobile */}
      {isMobile() && <WalletConnectButton />}

      {/* Always available */}
      <WalletConnectQRButton />
    </div>
  )
}

Resources ​

Troubleshooting ​

QR code not showing?

typescript
// Enable QR modal explicitly
const { connect } = useWalletConnect({ showQrModal: true })

Deep link not working?

typescript
// Check wallet app is installed
const isInstalled = await checkWalletInstalled('trust')
if (!isInstalled) {
  // Redirect to app store
}

Session expired?

typescript
// Implement auto-reconnect
useEffect(() => {
  if (sessionExpired) {
    disconnect()
    connect()
  }
}, [sessionExpired])

Next Steps ​

Built with Civra - Web3 Development Platform