npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@jim4565/dapp-wrapper-react

v1.4.1

Published

React components and hooks for @jim4565/dapp-wrapper with persistent wallet connection

Readme

@jim4565/dapp-wrapper-react

🚀 React components and hooks for @jim4565/dapp-wrapper with persistent wallet connection

Built with industry standards following wagmi/RainbowKit patterns and MetaMask-level persistence.

TypeScript React License: MIT


🎯 What This Library Does

This library extends your existing @jim4565/dapp-wrapper with:

Persistent Wallet Connection - Auto-reconnect after page refresh without popups
React Integration - Hooks, context, and components for seamless React usage
Cross-Page Persistence - Maintain connection across routes and page refreshes
3-Tier Reconnection Strategy - Silent → Session → Full Connect (industry standard)
Your Familiar API - Keep using contract.playGame(true).withValue("0.1") syntax
Real-time Balance - Automatic balance updates with 5-strategy fallback system

Key Benefit: Your existing contract interaction code works exactly the same, but now with React hooks and persistent connection!


🚀 Installation & Setup

1. Install the Package

npm install @jim4565/dapp-wrapper-react @jim4565/dapp-wrapper react react-dom

2. Critical Setup - Provider Configuration

⚠️ IMPORTANT: The provider MUST wrap your entire app to enable persistent connection across all pages.

Next.js Setup (pages/_app.tsx or app/layout.tsx)

// pages/_app.tsx (Pages Router)
import { IncentivWalletProvider } from '@jim4565/dapp-wrapper-react';
import type { AppProps } from 'next/app';

// Your existing contract ABIs
const gameABI = [
  {
    "inputs": [{"internalType": "bool", "name": "_isActive", "type": "bool"}],
    "name": "playGame",
    "outputs": [],
    "stateMutability": "payable",
    "type": "function"
  }
  // ... rest of your ABI
];

const contracts = [
  { name: "Game", address: "0x1234567890...", abi: gameABI },
  { name: "Lottery", address: "0x0987654321...", abi: lotteryABI }
];

export default function App({ Component, pageProps }: AppProps) {
  return (
    <IncentivWalletProvider
      config={{
        contracts,              // Register your contracts
        autoConnect: true,      // Auto-reconnect on app start
        reconnectOnMount: true, // Try reconnection when mounted
        pollingInterval: 30000  // Balance polling (30s)
      }}
      enableLogging={process.env.NODE_ENV === 'development'}
    >
      {/* Your entire app - ALL pages will have persistent wallet connection */}
      <Component {...pageProps} />
    </IncentivWalletProvider>
  );
}
// app/layout.tsx (App Router)
'use client';
import { IncentivWalletProvider } from '@jim4565/dapp-wrapper-react';

const contracts = [
  { name: "Game", address: "0x1234567890...", abi: gameABI }
];

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <body>
        <IncentivWalletProvider
          config={{
            contracts,
            autoConnect: true,
            reconnectOnMount: true,
            pollingInterval: 30000
          }}
          enableLogging={process.env.NODE_ENV === 'development'}
        >
          {/* All pages and components will have persistent wallet connection */}
          {children}
        </IncentivWalletProvider>
      </body>
    </html>
  );
}

React Router Setup (src/App.tsx)

// src/App.tsx
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import { IncentivWalletProvider } from '@jim4565/dapp-wrapper-react';
import { HomePage } from './pages/HomePage';
import { GamePage } from './pages/GamePage';
import { LeaderboardPage } from './pages/LeaderboardPage';

const contracts = [
  { name: "Game", address: "0x1234567890...", abi: gameABI }
];

function App() {
  return (
    <IncentivWalletProvider 
      config={{ 
        contracts,
        autoConnect: true,
        reconnectOnMount: true,
        pollingInterval: 30000
      }}
      enableLogging={process.env.NODE_ENV === 'development'}
    >
      <BrowserRouter>
        {/* All routes will have persistent wallet connection */}
        <Routes>
          <Route path="/" element={<HomePage />} />
          <Route path="/game" element={<GamePage />} />
          <Route path="/leaderboard" element={<LeaderboardPage />} />
        </Routes>
      </BrowserRouter>
    </IncentivWalletProvider>
  );
}

export default App;

📱 Basic Usage

Wallet Connection Hook

import { useIncentivWallet } from '@jim4565/dapp-wrapper-react';

function WalletInfo() {
  const { 
    address,           // Current wallet address
    isConnected,       // Connection status
    isConnecting,      // Loading state
    balance,           // Current balance in ETH
    isBalanceLoading,  // Balance loading state
    refreshBalance,    // Manual balance refresh
    connect,           // Connect function
    disconnect,        // Disconnect function
    error              // Any connection errors
  } = useIncentivWallet();

  if (!isConnected) {
    return (
      <button onClick={connect} disabled={isConnecting}>
        {isConnecting ? 'Connecting...' : 'Connect Wallet'}
      </button>
    );
  }

  return (
    <div>
      <p>Address: {address}</p>
      <p>Balance: {isBalanceLoading ? 'Loading...' : `${balance} ETH`}</p>
      <button onClick={disconnect}>Disconnect</button>
      <button onClick={refreshBalance}>Refresh Balance</button>
    </div>
  );
}

Contract Interaction

The beauty: Your existing contract interaction code works exactly the same!

import { useContract, useIncentivWallet } from '@jim4565/dapp-wrapper-react';
import { useState } from 'react';

function GameComponent() {
  const { isConnected } = useIncentivWallet();
  const { contract: gameContract, isLoading, error } = useContract("Game");
  const [isPlaying, setIsPlaying] = useState(false);

  const playGame = async () => {
    if (!gameContract || !isConnected) return;
    
    try {
      setIsPlaying(true);
      
      // Your exact @jim4565/dapp-wrapper syntax - no changes needed!
      await gameContract.playGame(true).withValue("0.1");
      
      alert('Game played successfully!');
    } catch (error) {
      console.error('Game failed:', error);
      alert('Game failed: ' + error.message);
    } finally {
      setIsPlaying(false);
    }
  };

  if (!isConnected) {
    return <p>Connect your wallet to play</p>;
  }

  if (isLoading) {
    return <p>Loading contract...</p>;
  }

  if (error) {
    return <p>Error loading contract: {error.message}</p>;
  }

  return (
    <button onClick={playGame} disabled={isPlaying}>
      {isPlaying ? 'Playing...' : 'Play Game (0.1 ETH)'}
    </button>
  );
}

Ready-to-Use Components

import { ConnectButton, WalletStatus } from '@jim4565/dapp-wrapper-react';

function Navigation() {
  return (
    <nav>
      {/* Ready-to-use wallet components */}
      <WalletStatus showBalance={true} />
      <ConnectButton />
    </nav>
  );
}

⚠️ Critical Navigation Rule

ALWAYS use framework routing components to preserve wallet connection:

// ✅ CORRECT - Preserves wallet connection
import Link from 'next/link';               // Next.js
// import { Link } from 'react-router-dom';  // React Router

function Navigation() {
  return (
    <nav>
      <Link href="/game">Game</Link>           {/* ✅ Client-side routing */}
      <Link href="/leaderboard">Leaderboard</Link>  {/* ✅ Wallet stays connected */}
    </nav>
  );
}

// ❌ WRONG - Forces page reload and wallet reconnection
function BadNavigation() {
  return (
    <nav>
      <a href="/game">Game</a>                 {/* ❌ Page reload */}
      <a href="/leaderboard">Leaderboard</a>   {/* ❌ Wallet must reconnect */}
    </nav>
  );
}

🔧 API Reference

Main Hooks

useIncentivWallet()

Main wallet hook providing connection state and actions.

const {
  // Connection State
  address,           // string | undefined
  isConnected,       // boolean
  isConnecting,      // boolean
  error,            // Error | null
  
  // Balance State
  balance,           // string | undefined - current balance in ETH
  isBalanceLoading,  // boolean - balance loading state
  refreshBalance,    // () => Promise<void> - manual balance refresh
  
  // Actions
  connect,          // () => Promise<void>
  disconnect,       // () => void
  reconnect         // () => Promise<void>
} = useIncentivWallet();

useContract(contractName)

Access registered smart contracts.

const { 
  contract,     // Contract instance with your methods
  isLoading,    // boolean
  error         // Error | null
} = useContract("GameContract");

// Use your contract exactly as before
await contract.playGame(true).withValue("0.1");

Components

<ConnectButton />

Professional wallet connection button.

<ConnectButton
  showAddress={true}
  showBalance={false}
  size="medium"
  variant="primary"
/>

<WalletStatus />

Comprehensive wallet status display.

<WalletStatus
  showBalance={true}
  balanceDecimals={4}
  showFullAddress={false}
  size="medium"
/>

🏗️ How Persistence Works

3-Tier Reconnection Strategy

The library implements a professional reconnection strategy:

  1. Tier 1: Silent Check - Check existing session without popups
  2. Tier 2: Session Restore - Restore previous session silently
  3. Tier 3: Full Connect - Show connection popup only if user connected before

This ensures:

  • No unwanted popups for first-time users
  • Seamless reconnection for returning users
  • Fallback strategies if silent methods fail

What Gets Persisted

  • Wallet address - Securely stored in localStorage
  • Connection status - Whether user was connected
  • Ever connected flag - Prevents popups for new users
  • Data expiration - Auto-clears old data (30 days)

🔍 Troubleshooting

Provider Not Found Error

// ❌ Wrong: Component used outside provider
function App() {
  return (
    <div>
      <MyWalletComponent /> {/* Error: hook called outside provider */}
      <IncentivWalletProvider>
        <OtherComponents />
      </IncentivWalletProvider>
    </div>
  );
}

// ✅ Correct: All components inside provider
function App() {
  return (
    <IncentivWalletProvider>
      <div>
        <MyWalletComponent /> {/* Works: inside provider */}
        <OtherComponents />
      </div>
    </IncentivWalletProvider>
  );
}

Wallet Disconnects When Navigating

// ❌ Problem: Using <a> tags forces page reload
<a href="/game">Game</a>        // Page reload → wallet disconnect

// ✅ Solution: Use framework routing components
<Link href="/game">Game</Link>  // Client-side → wallet preserved

Auto-Reconnect Not Working

// Debug what's stored:
import { PersistenceService } from '@jim4565/dapp-wrapper-react';

const { address, wasConnected, hasEverConnected } = PersistenceService.loadWalletData();
console.log('Stored data:', { address, wasConnected, hasEverConnected });

// For auto-reconnect to work, you need:
// ✅ address: "0x..." (valid address)
// ✅ wasConnected: true (was connected last session)  
// ✅ hasEverConnected: true (user has connected before)

Debug Mode

Enable logging to see what's happening:

<IncentivWalletProvider 
  config={config}
  enableLogging={true}  // ✅ Enable debug logs
>

🎯 Migration from Raw @jim4565/dapp-wrapper

// ❌ Before: Raw wrapper usage
import { IncentivWrapper } from '@jim4565/dapp-wrapper';

const wrapper = new IncentivWrapper();
await wrapper.connect();
wrapper.registerContract("Game", address, abi);
const contract = wrapper.getContract("Game");
await contract.playGame(true).withValue("0.1");
// ✅ After: React wrapper (no changes to contract calls!)
import { IncentivWalletProvider, useContract } from '@jim4565/dapp-wrapper-react';

// 1. Wrap your app
<IncentivWalletProvider config={{ contracts: [...] }}>
  <App />
</IncentivWalletProvider>

// 2. Use hooks in components
function GameComponent() {
  const { contract } = useContract("Game");
  
  // Same syntax! No changes needed!
  await contract.playGame(true).withValue("0.1");
}

Benefits of migrating:

  • ✅ Persistent connection across pages
  • ✅ Automatic reconnection
  • ✅ React hooks and components
  • ✅ TypeScript improvements
  • Your contract code stays exactly the same!

📄 License

MIT License - see LICENSE file for details.


Built with ❤️ for the Incentiv ecosystem

Making Web3 development as easy as Web2