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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@wheelx-widget/widget-native

v0.1.4

Published

WheelX Widget Native - React Native WebView version of cross-chain swap and bridge trading component, wallet connection via RN bridge

Readme

@wheelx-widget/widget-native

The React Native WebView version of the WheelX Widget. Designed to be loaded inside a react-native-webview, it communicates with the host RN app via postMessage and CustomEvent for wallet connections, EIP-1193 bridging, and transaction signing.

Wallet Framework: widget-native relies exclusively on Reown (reown.com) on the RN side. The host app must integrate @reown/appkit-react-native to provide wallet functionality.

Features

  • 🔄 Cross-chain Swap & Bridge — Full trading flow with quote polling, gas estimation, approval handling
  • 👛 Reown Wallet Integration — All wallet operations forwarded to RN host's Reown AppKit via WebView bridge
  • 🌐 Multi-chain Support — EVM (50+ chains) and Solana with unified bridge protocol
  • 🎨 Configurable Theme — 14 color tokens for custom skinning
  • 📦 Framework Agnostic — Works with any React framework (Next.js, Vite, Create React App, etc.)
  • 🔌 Configurable Default Tokens — Set default from/to chains and tokens
  • 📱 React Native Ready — Built specifically for RN WebView embedding with bridge protocol

Architecture

┌──────────────────────────────────────────────────────────┐
│  WebView (widget-native)                                 │
│                                                          │
│  WheelXProvider                                          │
│  ├── ExternalWalletBridge  ← CustomEvent('wheelx:walletState') │
│  ├── Eip1193BridgeProvider ← window.__wheelx_ethereum    │
│  ├── Eip1193AutoConnector  → wagmi auto-connect          │
│  └── WheelXWidget (MainForm)                             │
│      ├── FormButton    → useWalletConnectModal           │
│      ├── TxState       → transaction flow                │
│      └── Solana Tx     → useSolanaSendTransaction        │
│                                                          │
└──────────────────────┬───────────────────────────────────┘
                       │ postMessage (Widget → RN)
                       │ injectJavaScript (RN → Widget)
┌──────────────────────▼───────────────────────────────────┐
│  React Native Host (e.g. desafa-master)                  │
│                                                          │
│  AppContent.tsx                                          │
│  ├── WebView component                                   │
│  ├── onMessage handler → routes bridge messages          │
│  └── injectLatestState() → pushes wallet state to WebView│
│                                                          │
│  BridgeHandler.ts                                        │
│  ├── buildWalletState() — constructs wallet state        │
│  ├── handleEip1193Request() — JSON-RPC via Reown         │
│  └── handleSolanaSignRequest() — sign + broadcast        │
│                                                          │
│  Reown AppKit (@reown/appkit-react-native)               │
│  ├── EVM: EthersAdapter + WagmiAdapter                   │
│  ├── Solana: SolanaAdapter + Phantom/Solflare            │
│  └── Wallet Connect / Disconnect / Chain Switch          │
│                                                          │
└──────────────────────────────────────────────────────────┘

Installation

npm install @wheelx-widget/widget-native @chakra-ui/react @emotion/react

Note: @chakra-ui/react and @emotion/react are required peer dependencies.

Quick Start

// main.tsx — wrap with ChakraProvider
import { ChakraProvider, defaultSystem } from '@chakra-ui/react'
import { createRoot } from 'react-dom/client'
import App from './App'

createRoot(document.getElementById('root')!).render(
  <ChakraProvider value={defaultSystem}>
    <App />
  </ChakraProvider>
)
// App.tsx
// Import the CSS for animations (required!)
import '@wheelx-widget/widget-native/styles.css'
import { WheelXProvider, WheelXWidget } from '@wheelx-widget/widget-native'

function App() {
  return (
    <WheelXProvider>
      <WheelXWidget />
    </WheelXProvider>
  )
}

RN ↔ WebView Communication Protocol

The widget communicates with the RN host through a bidirectional bridge.

RN → Widget (via injectJavaScript + CustomEvent)

The RN host injects JavaScript into the WebView to dispatch CustomEvents that the widget listens for.

| CustomEvent | Payload | Description | | ---------------------- | ---------------------------------------------------------------------- | ---------------------------- | | wheelx:walletState | { evmAddress, solanaAddress, chainId, walletType, isConnected, ... } | Full wallet connection state | | wheelx:eip1193Result | { requestId, result, error } | EIP-1193 JSON-RPC response | | wheelx:solanaResult | { requestId, signature, error } | Solana signing result |

RN injection example:

// Inside RN host AppContent.tsx
webViewRef.current?.injectJavaScript(`
  window.dispatchEvent(new CustomEvent('wheelx:walletState', {
    detail: {
      evmAddress: '0x...',
      solanaAddress: null,
      chainId: 8453,
      chainIdStr: '8453',
      walletType: 'evm',
      isConnected: true
    }
  }));
  true;
`)

Widget → RN (via postMessage)

The widget sends JSON messages to RN through window.ReactNativeWebView.postMessage().

| Message Type | Payload | Description | | --------------------------- | ---------------------------------------- | ----------------------------------------------------- | | WHEELX_REQUEST_CONNECT | { type, timestamp, chainType } | Request RN to open Reown wallet connect modal | | WHEELX_REQUEST_DISCONNECT | { type, timestamp } | Request RN to disconnect wallet | | WHEELX_EIP1193_REQUEST | { type, requestId, method, params } | EIP-1193 JSON-RPC request (eth_sendTransaction, etc.) | | WHEELX_SOLANA_SIGN | { type, requestId, txMessage, sender } | Solana transaction signing request |

RN handler example:

// Inside RN host
const handleMessage = (event: WebViewMessageEvent) => {
  const data = JSON.parse(event.nativeEvent.data);
  switch (data.type) {
    case 'WHEELX_REQUEST_CONNECT':
      appKit.open({ view: 'Connect' });
      break;
    case 'WHEELX_EIP1193_REQUEST':
      handleEip1193Request(data);
      break;
    case 'WHEELX_SOLANA_SIGN':
      handleSolanaSignRequest(data);
      break;
  }
};

<WebView onMessage={handleMessage} ... />

EIP-1193 Bridge (EVM Signing)

  1. wagmi calls window.ethereum.request({ method: 'eth_sendTransaction', params: [...] })
  2. This is intercepted by window.__wheelx_ethereum (injected by RN)
  3. The request is forwarded to RN via WHEELX_EIP1193_REQUEST postMessage
  4. RN's Reown AppKit processes the request via walletProvider.request()
  5. Result is injected back as wheelx:eip1193Result CustomEvent
  6. The original Promise resolves/rejects accordingly

Key detail: Error codes (e.g., 4902 for chain not added) are preserved, enabling wagmi's automatic chain-switch/add fallback logic.

Solana Signing Flow

  1. Widget calls requestSolanaSignFromRN(txMessage, sender)
  2. Sends WHEELX_SOLANA_SIGN via postMessage with requestId
  3. Awaits wheelx:solanaResult CustomEvent matching the requestId
  4. RN's Reown AppKit signs via solanaProvider.request({ method: 'solana_signTransaction' })
  5. RN broadcasts the signed transaction to the network
  6. Signature is injected back to WebView, resolving the Promise
  7. Timeout: 5 minutes

State Injection Strategy

To ensure the WebView always has the correct wallet state, RN should inject at multiple timepoints:

  1. Before page loadinjectedJavaScriptBeforeContentLoaded sets window.__wheelx_initial_wallet_state__
  2. On load (0ms) — Immediate state injection after page completes
  3. On load (500ms) — EIP-1193 provider + wallet state injection (for Reown async session restore)
  4. On load (2000ms) — Fallback injection (belated session restore)
  5. Event-driven — Inject state on CONNECT_SUCCESS / DISCONNECT_SUCCESS / accountsChanged

Reown Integration on RN Side

The widget-native expects the RN host to integrate Reown AppKit. Here's a minimal setup based on the reference implementation:

1. Install Dependencies

npm install @reown/appkit-react-native @reown/appkit-ethers-react-native @reown/appkit-solana-react-native react-native-webview

2. Configure AppKit

// AppKitConfig.ts
import { AppKit } from '@reown/appkit-react-native'
import { EthersAdapter } from '@reown/appkit-ethers-react-native'
import { SolanaAdapter } from '@reown/appkit-solana-react-native'

const evmAdapter = new EthersAdapter()
const solanaAdapter = new SolanaAdapter()

const appKit = new AppKit({
  adapters: [evmAdapter, solanaAdapter],
  projectId: 'your-reown-project-id',
  networks: [
    /* your chains */
  ]
})

3. Wrap with Providers

// App.tsx
import { AppKitProvider } from '@reown/appkit-react-native'

export default function App() {
  return (
    <SafeAreaProvider>
      <AppKitProvider>
        <AppContent />
      </AppKitProvider>
    </SafeAreaProvider>
  )
}

4. Bridge Handler

Implement a bridge handler that:

  • Listens for postMessage events from the WebView
  • Routes WHEELX_REQUEST_CONNECTappKit.open()
  • Routes WHEELX_EIP1193_REQUESTwalletProvider.request()
  • Routes WHEELX_SOLANA_SIGN → Solana provider sign + broadcast
  • Injects wallet state on connect/disconnect/account changes

Refer to the reference implementation at F:\react-native-app\desafa-master for a complete working example.

Configuration

Theme Customization

Customize 14 visual tokens:

<WheelXProvider
  config={{
    theme: {
      defaultTextColor: '#1A1D26',
      highlightTextColor: '#007B9D',
      weakTextColor: '#5D6270',
      defaultBgColor: '#FFFFFF',
      highlightBgColor: '#E6F7FB',
      assistBgColor: '#F5F6F8',
      defaultBorderColor: '#E2E4E8',
      highlightBorderColor: '#007B9D',
      highlightButtonBg: '#007B9D',
      highlightButtonText: '#FFFFFF',
      normalButtonBg: '#F5F6F8',
      normalButtonText: '#1A1D26',
      disabledButtonBg: '#E2E4E8',
      disabledButtonText: '#A0A4AE'
    }
  }}
>
  <WheelXWidget />
</WheelXProvider>

Default Chain/Token Configuration

<WheelXProvider
  config={{
    from: {
      chains: [8453, 1], // Only show Base and Ethereum as from chains
      defaultChainId: 8453 // Default to Base
    },
    to: {
      chains: [1868], // Only show Soneium as to chain
      defaultChainId: 1868 // Default to Soneium
    }
  }}
>
  <WheelXWidget />
</WheelXProvider>

Fixed Recipient Address (Different Address)

Configure fixed recipient addresses for EVM or Solana chains. When set, the widget will:

  • Display the configured address in the To field instead of showing "Connect Wallet"
  • Query the token balance of the configured address
  • Skip wallet connection for the target chain (only the source chain wallet is required for signing)
  • Show GetGas when the configured address has insufficient native token balance

This is useful for exchange or merchant scenarios where funds should always be sent to a specific address regardless of which wallet the user connects.

Priority order for recipient address:

  1. Configured fixed address (evmDifferentAddress / solanaDifferentAddress)
  2. User manually entered different address (via Different Address dialog)
  3. Connected wallet address
<WheelXProvider
  config={{
    evmDifferentAddress: '0xAbCdEf1234567890...', // Fixed recipient for EVM chains (0x format)
    solanaDifferentAddress: '9zQX...' // Fixed recipient for Solana chains (base58 format)
  }}
>
  <WheelXWidget />
</WheelXProvider>

Note: You can configure one, both, or neither. The widget will only use the relevant one based on the selected To chain.

API Reference

<WheelXProvider>

The root provider component that sets up wallet connections, React Query, and theme.

| Prop | Type | Default | Description | | ---------- | ----------------- | ------- | -------------------- | | config | WidgetConfig | {} | Widget configuration | | children | React.ReactNode | — | Child components |

Key components inside WheelXProvider:

  • ExternalWalletBridge — Listens for RN-injected wallet state via wheelx:walletState CustomEvent
  • Eip1193BridgeProvider — Detects window.__wheelx_ethereum EIP-1193 bridge provider from RN
  • Eip1193AutoConnector — Auto-connects wagmi to the EIP-1193 bridge when available
  • ChainsAndTokensLoader — Fetches and caches chain/token metadata from API

<WheelXWidget>

The main trading widget component.

| Prop | Type | Default | Description | | -------- | -------------- | ------- | -------------------- | | config | WidgetConfig | {} | Widget configuration |

WidgetConfig

interface WidgetConfig {
  theme?: Partial<WidgetTheme>

  from?: {
    chains?: number[]
    defaultChainId?: number
    chainTokens?: Record<number, TokenConfig[]>
    defaultToken?: { chainId: number; address: string }
  }

  to?: {
    chains?: number[]
    defaultChainId?: number
    chainTokens?: Record<number, TokenConfig[]>
    defaultToken?: { chainId: number; address: string }
  }

  routerCallback?: {
    onPathnameChange?: (pathname: string) => void
    onRouteLeave?: () => void
    getCurrentPathname?: () => string
  }

  /**
   * Deep link handler. When the widget runs inside a WebView, this passes wallet deep link URLs
   * to the host app for handling (e.g., opening the wallet app via Linking.openURL).
   *
   * If not provided, the widget will automatically attempt to communicate via
   * ReactNativeWebView.postMessage or custom DOM events.
   */
  onDeepLink?: (url: string) => void

  /** Fixed receive address for EVM chains (0x format). When set, this address is used as the recipient on EVM destination chains. */
  evmDifferentAddress?: string
  /** Fixed receive address for Solana chains (base58 format). When set, this address is used as the recipient on Solana destination chains. */
  solanaDifferentAddress?: string
}

WidgetTheme

interface WidgetTheme {
  defaultTextColor: string
  highlightTextColor: string
  weakTextColor: string
  defaultBgColor: string
  highlightBgColor: string
  assistBgColor: string
  defaultBorderColor: string
  highlightBorderColor: string
  highlightButtonBg: string
  highlightButtonText: string
  normalButtonBg: string
  normalButtonText: string
  disabledButtonBg: string
  disabledButtonText: string
  /** Color palette for the GetGas switch component. e.g. 'purple', 'blue', 'green'. Default: 'purple' */
  getGasSwitchColorPalette?: string
}

Exported Bridge Utilities

// External Wallet State (injected by RN)
export type { ExternalWalletState } from '@wheelx-widget/widget-native'
export { ExternalWalletBridge } from '@wheelx-widget/widget-native'

// EIP-1193 Bridge Provider
export {
  Eip1193BridgeProvider,
  useEip1193Bridge,
  isEip1193BridgeAvailable
} from '@wheelx-widget/widget-native'
export type { BridgeProviderStatus } from '@wheelx-widget/widget-native'

// Wallet Connect via RN Bridge
export {
  useWalletConnectModal,
  WHEELX_MESSAGES
} from '@wheelx-widget/widget-native'

// Platform detection
export {
  isReactNativeWebView,
  isWebView,
  isMobileDevice
} from '@wheelx-widget/widget-native'

Communication Protocol Reference

ExternalWalletState

The wallet state object injected by RN via wheelx:walletState CustomEvent:

interface ExternalWalletState {
  // EVM
  evmAddress: string | null
  evmWalletName?: string | null // Per-chain wallet name (e.g. "MetaMask")
  evmWalletIcon?: string | null // Per-chain wallet icon URL

  // Solana
  solanaAddress: string | null
  solanaWalletName?: string | null // Per-chain wallet name (e.g. "Phantom")
  solanaWalletIcon?: string | null // Per-chain wallet icon URL

  // Chain info
  chainId: number | null
  chainIdStr: string | null
  walletType: 'evm' | 'solana' | null

  // Legacy (single wallet — kept for backward compat)
  walletName: string | null
  walletIcon: string | null

  // Connection status
  isConnected: boolean
}

Pre-injected State

RN can pre-inject wallet state before the widget's JavaScript runs:

// In RN: injectedJavaScriptBeforeContentLoaded
window.__wheelx_initial_wallet_state__ = {
  evmAddress: '0x...',
  chainId: 8453
  // ...full ExternalWalletState
}

The ExternalWalletBridge component will pick this up on mount and remove it from window.

Demo

The demo page is available at https://widget.wheelx.fi/widget-native.

Troubleshooting

| Problem | Symptom | Solution | |----------|---------|----------| | Forgot to import CSS | Component works but has no styling/animations | Add import '@wheelx-widget/widget-native/styles.css' in your entry file | | Wallet not connecting | Widget shows "Connect Wallet" but never connects | Ensure the RN host properly implements the bridge — inject wheelx:walletState CustomEvent on connect/disconnect, and handle WHEELX_REQUEST_CONNECT postMessage | | EIP-1193 transactions fail | Transaction signing hangs or errors out | Verify window.__wheelx_ethereum provider is injected and routes WHEELX_EIP1193_REQUEST to Reown's walletProvider.request(). Check that error codes (e.g., 4902) are preserved | | React hooks errors | "Invalid hook call" or context mismatch | You may have duplicate React instances. Ensure react and react-dom are peer dependencies in your project, and use a single version (18 or 19) |

License

MIT