> ## Documentation Index
> Fetch the complete documentation index at: https://docs.dynamic.xyz/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Rhinestone

> Integrate Dynamic signers with Rhinestone smart accounts.

<Card title="Recommended: JavaScript SDK with React Hooks" icon="react" color="#4779FE">
  For new React apps, we recommend the JavaScript SDK with React Hooks (`@dynamic-labs-sdk/react-hooks`) instead of the legacy React SDK documented here. The JS SDK comes with many benefits such as a much smaller bundle size and other optimizations. Use the [React quickstart (JavaScript SDK)](/javascript/reference/react-quickstart) to get started.
</Card>

## Prerequisites

* A Dynamic account and project (with Ethereum enabled)
* React application setup

## Installation

Install the required dependencies:

```bash theme={"system"}
npm install @dynamic-labs/sdk-react-core @dynamic-labs/ethereum @rhinestone/sdk
```

## Setup Dynamic Provider

First, set up the Dynamic provider in your React application:

```tsx theme={"system"}
import { DynamicContextProvider } from '@dynamic-labs/sdk-react-core'
import { EthereumWalletConnectors } from '@dynamic-labs/ethereum'

function App() {
  return (
    <DynamicContextProvider
      settings={{
        environmentId: process.env.NEXT_PUBLIC_DYNAMIC_ENVIRONMENT_ID,
        walletConnectors: [EthereumWalletConnectors],
      }}
    >
      {/* Your app components */}
    </DynamicContextProvider>
  )
}
```

## Create a Dynamic Integration Hook

Create a custom hook that integrates Dynamic wallets with Rhinestone accounts. This demonstrates the core pattern: **get the wallet client, then pass it to Rhinestone**.

```tsx theme={"system"}
import { useDynamicContext } from '@dynamic-labs/sdk-react-core'
import { createRhinestoneAccount } from "@rhinestone/sdk"
import { isEthereumWallet } from '@dynamic-labs/ethereum'
import { useState, useEffect, useMemo } from 'react'

export function useGlobalWallet() {
  const { primaryWallet } = useDynamicContext();
  const [rhinestoneAccount, setRhinestoneAccount] = useState(null)

  // Memoize the wallet address to use as a stable dependency
  const walletAddress = useMemo(() => {
    return primaryWallet?.address
  }, [primaryWallet?.address])

  useEffect(() => {
    let isMounted = true

    async function initializeAccount() {
      if (!primaryWallet || !isEthereumWallet(primaryWallet) || !walletAddress) {
        setRhinestoneAccount(null)
        return
      }

      try {
        // Get the wallet client asynchronously
        const walletClient = await primaryWallet.getWalletClient()
        
        if (!isMounted) return

        // Dynamic sometimes needs address explicitly added to the client
        const walletClientWithAddress = {
          ...walletClient,
          address: walletAddress,
        };

        // Pass the wallet client (from Dynamic) to Rhinestone
        // Rhinestone wraps it with cross-chain transaction capabilities
        const account = await createRhinestoneAccount({
          owners: {
            type: "ecdsa",
            accounts: [walletClientWithAddress as any], // client from Dynamic
          },
          rhinestoneApiKey: import.meta.env.VITE_RHINESTONE_API_KEY,
        })

        if (isMounted) {
          setRhinestoneAccount(account)
        }
      } catch (error) {
        console.error('Failed to create Rhinestone account:', error)
        if (isMounted) {
          setRhinestoneAccount(null)
        }
      }
    }

    initializeAccount()

    // Cleanup function to prevent setting state on unmounted component
    return () => {
      isMounted = false
    }
  }, [primaryWallet?.address])

  return { rhinestoneAccount, address: primaryWallet?.address }
}
```

## Usage

### Basic Component Integration

Use the hook in your React components:

```tsx {5-7} theme={"system"}
import { useGlobalWallet } from './hooks/useGlobalWallet'
import { useDynamicContext } from '@dynamic-labs/sdk-react-core'

function WalletDashboard() {
  const { setShowAuthFlow } = useDynamicContext()
  const { rhinestoneAccount, address } = useGlobalWallet()

  if (!rhinestoneAccount) {
    return (
      <button onClick={() => setShowAuthFlow(true)}>
        Connect Wallet
      </button>
    )
  }

  return (
    <div>
      <h2>Connected: {address}</h2>
      <p>Smart Account: {rhinestoneAccount.getAddress()}</p>
    </div>
  )
}
```

### Cross-Chain Transactions

Send transactions using the Dynamic-connected wallet:

```tsx {2-14} theme={"system"}
async function handleCrossChainTransfer() {
  const transaction = await rhinestoneAccount.sendTransaction({
    sourceChains: [baseSepolia],
    targetChain: arbitrumSepolia,
    calls: [
      {
        to: "USDC",
        data: encodeFunctionData({
          abi: erc20Abi,
          functionName: "transfer",
          args: ["0xrecipient", parseUnits("10", 6)],
        }),
      },
    ],
    tokenRequests: [
      {
        address: "USDC",
        amount: parseUnits("10", 6),
      },
    ],
  })
}
```

## Environment Variables

Make sure to set the following environment variables:

```bash theme={"system"}
NEXT_PUBLIC_DYNAMIC_ENVIRONMENT_ID=your_dynamic_environment_id
NEXT_PUBLIC_RHINESTONE_API_KEY=your_rhinestone_api_key
```

## Next Steps

* **See it in action**: Check out our [complete Dynamic example](https://github.com/rhinestonewtf/e2e-examples/tree/main/dynamic) for a full working app
* Learn more about [Rhinestone's features](https://docs.rhinestone.dev/)
