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

# Dynamic

> Integrate Dynamic signers with Rhinestone smart accounts.

## Overview

Dynamic provides embedded wallet infrastructure that enables seamless user onboarding and wallet management. This guide shows you how to integrate Dynamic signers with Rhinestone smart accounts to create a unified, cross-chain wallet experience.

**How it works:** Dynamic handles user authentication and wallet connections, providing wagmi-compatible clients. We use wagmi hooks to access these clients and pass them to Rhinestone's SDK, which wraps them with cross-chain capabilities.

## Prerequisites

* A Dynamic account and project
* Dynamic Environment ID
* React application setup

<Steps>
  <Step title="Install Dependencies">
    Install the required dependencies:

    <CodeGroup>
      ```bash npm theme={null}
      npm install @dynamic-labs/sdk-react-core @dynamic-labs/ethereum @dynamic-labs/wagmi-connector @tanstack/react-query @rhinestone/sdk viem wagmi
      ```

      ```bash pnpm theme={null}
      pnpm add @dynamic-labs/sdk-react-core @dynamic-labs/ethereum @dynamic-labs/wagmi-connector @tanstack/react-query @rhinestone/sdk viem wagmi
      ```

      ```bash bun theme={null}
      bun install @dynamic-labs/sdk-react-core @dynamic-labs/ethereum @dynamic-labs/wagmi-connector @tanstack/react-query @rhinestone/sdk viem wagmi
      ```
    </CodeGroup>
  </Step>

  <Step title="Set Up Dynamic Provider">
    Set up the Dynamic provider with wagmi and TanStack Query in your React application:

    ```tsx theme={null}
    import { DynamicContextProvider } from '@dynamic-labs/sdk-react-core'
    import { EthereumWalletConnectors } from '@dynamic-labs/ethereum'
    import { DynamicWagmiConnector } from '@dynamic-labs/wagmi-connector'
    import { createConfig, WagmiProvider } from 'wagmi'
    import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
    import { http } from 'viem'
    import { mainnet, polygon, arbitrum, base } from 'viem/chains'

    const wagmiConfig = createConfig({
      chains: [mainnet, polygon, arbitrum, base],
      multiInjectedProviderDiscovery: false,
      transports: {
        [mainnet.id]: http(),
        [polygon.id]: http(),
        [arbitrum.id]: http(),
        [base.id]: http(),
      },
    })

    const queryClient = new QueryClient()

    function App() {
      return (
        <DynamicContextProvider
          settings={{
            environmentId: process.env.NEXT_PUBLIC_DYNAMIC_ENVIRONMENT_ID,
            walletConnectors: [EthereumWalletConnectors],
          }}
        >
          <WagmiProvider config={wagmiConfig}>
            <QueryClientProvider client={queryClient}>
              <DynamicWagmiConnector>
                {/* Your app components */}
              </DynamicWagmiConnector>
            </QueryClientProvider>
          </WagmiProvider>
        </DynamicContextProvider>
      )
    }
    ```
  </Step>

  <Step title="Create Integration Hook">
    Create a production-ready hook that integrates Dynamic wallets with Rhinestone accounts. This demonstrates the core pattern: **get the wallet client from wagmi, then pass it to Rhinestone**.

    <Note>Dynamic automatically populates wagmi's `useAccount()` and `useWalletClient()` hooks when users connect their wallets.</Note>

    ```tsx theme={null}
    import { useState, useEffect } from 'react'
    import { useAccount, useWalletClient } from "wagmi"
    import { RhinestoneSDK, walletClientToAccount } from "@rhinestone/sdk"

    interface GlobalWalletState {
      rhinestoneAccount: any | null
      address: string | null
      isLoading: boolean
      error: string | null
      isConnected: boolean
    }

    export function useGlobalWallet(): GlobalWalletState & {
      reconnect: () => Promise<void>
    } {
      const { address, isConnected } = useAccount()
      const { data: walletClient } = useWalletClient()
      const [state, setState] = useState<GlobalWalletState>({
        rhinestoneAccount: null,
        address: null,
        isLoading: false,
        error: null,
        isConnected: false,
      })

      const initializeAccount = async () => {
        if (!isConnected || !address || !walletClient) {
          setState(prev => ({
            ...prev,
            rhinestoneAccount: null,
            address: null,
            isConnected: false,
            error: null,
          }))
          return
        }

        setState(prev => ({ ...prev, isLoading: true, error: null }))

        try {
          // Ensure wallet client has address property for Rhinestone compatibility
          const wrappedWalletClient = walletClientToAccount(walletClient)

          const apiKey = process.env.NEXT_PUBLIC_RHINESTONE_API_KEY
          if (!apiKey) {
            throw new Error('NEXT_PUBLIC_RHINESTONE_API_KEY is not configured')
          }

          // Create Rhinestone account using Dynamic's wallet client
          const rhinestone = new RhinestoneSDK({
            apiKey,
          })
          const account = await rhinestone.createAccount({
            owners: {
              type: "ecdsa",
              accounts: [wrappedWalletClient],
            },
          })

          setState(prev => ({
            ...prev,
            rhinestoneAccount: account,
            address,
            isConnected: true,
            isLoading: false,
          }))
        } catch (error) {
          console.error('Failed to initialize Rhinestone account:', error)
          setState(prev => ({
            ...prev,
            error: error instanceof Error ? error.message : 'Failed to initialize account',
            isLoading: false,
            isConnected: false,
          }))
        }
      }

      useEffect(() => {
        initializeAccount()
      }, [isConnected, address, walletClient])

      return {
        ...state,
        reconnect: initializeAccount,
      }
    }
    ```
  </Step>
</Steps>

## Usage

### Basic Component Integration

Use the enhanced hook in your React components with proper loading and error states:

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

function WalletDashboard() {
  const { setShowAuthFlow } = useDynamicContext()
  const {
    rhinestoneAccount,
    address,
    isLoading,
    error,
    isConnected,
    reconnect
  } = useGlobalWallet()

  // Handle loading state
  if (isLoading) {
    return (
      <div className="flex items-center justify-center p-8">
        <div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600"></div>
        <span className="ml-2">Setting up your global wallet...</span>
      </div>
    )
  }

  // Handle error state
  if (error) {
    return (
      <div className="bg-red-50 border border-red-200 rounded-lg p-4">
        <h3 className="text-red-800 font-medium">Wallet Setup Error</h3>
        <p className="text-red-600 text-sm mt-1">{error}</p>
        <button
          onClick={reconnect}
          className="mt-2 px-3 py-1 bg-red-600 text-white rounded text-sm hover:bg-red-700"
        >
          Try Again
        </button>
      </div>
    )
  }

  // Handle disconnected state
  if (!isConnected || !rhinestoneAccount) {
    return (
      <div className="text-center p-8">
        <h2 className="text-xl font-semibold text-gray-900 mb-4">
          Connect Your Wallet
        </h2>
        <p className="text-gray-600 mb-6">
          Connect with Dynamic to access cross-chain functionality
        </p>
        <button
          onClick={() => setShowAuthFlow(true)}
          className="px-6 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700"
        >
          Connect Wallet
        </button>
      </div>
    )
  }

  // Connected state
  return (
    <div className="bg-green-50 border border-green-200 rounded-lg p-6">
      <h2 className="text-lg font-semibold text-green-900 mb-2">
        Wallet Connected
      </h2>
      <div className="space-y-2 text-sm">
        <p><strong>EOA Address:</strong> {address}</p>
        <p><strong>Smart Account:</strong> {rhinestoneAccount.getAddress()}</p>
        <p className="text-green-700">Ready for cross-chain transactions!</p>
      </div>
    </div>
  )
}
```

### Cross-Chain Transactions

Send transactions using the Dynamic-connected wallet with proper error handling:

```tsx theme={null}
import { useState } from 'react'
import { encodeFunctionData, parseUnits, erc20Abi } from 'viem'
import { baseSepolia, arbitrumSepolia } from 'viem/chains'

function CrossChainTransfer({ rhinestoneAccount }) {
  const [isTransacting, setIsTransacting] = useState(false)
  const [txResult, setTxResult] = useState(null)
  const [error, setError] = useState(null)

  const handleCrossChainTransfer = async () => {
    if (!rhinestoneAccount) return

    setIsTransacting(true)
    setError(null)
    setTxResult(null)

    try {
      const transaction = await rhinestoneAccount.sendTransaction({
        sourceChains: [baseSepolia],
        targetChain: arbitrumSepolia,
        calls: [
          {
            to: "USDC", // This resolves to the USDC address on the target chain
            data: encodeFunctionData({
              abi: erc20Abi,
              functionName: "transfer",
              args: ["0xrecipient", parseUnits("10", 6)],
            }),
          },
        ],
        tokenRequests: [
          {
            address: "USDC",
            amount: parseUnits("10", 6),
          },
        ],
      })

      // Wait for transaction execution
      const result = await rhinestoneAccount.waitForExecution(transaction)

      setTxResult({
        id: transaction.id,
        hash: result.transactionHash,
        status: 'success',
      })
    } catch (err) {
      console.error('Transaction failed:', err)
      setError(err instanceof Error ? err.message : 'Transaction failed')
    } finally {
      setIsTransacting(false)
    }
  }

  return (
    <div className="space-y-4">
      <button
        onClick={handleCrossChainTransfer}
        disabled={isTransacting || !rhinestoneAccount}
        className="px-4 py-2 bg-blue-600 text-white rounded disabled:opacity-50"
      >
        {isTransacting ? 'Sending...' : 'Send 10 USDC (Base → Arbitrum)'}
      </button>

      {error && (
        <div className="text-red-600 text-sm">
          Error: {error}
        </div>
      )}

      {txResult && (
        <div className="text-green-600 text-sm">
          Transaction successful!
          <a
            href={`https://arbiscan.io/tx/${txResult.hash}`}
            target="_blank"
            rel="noopener noreferrer"
            className="underline ml-1"
          >
            View on Arbiscan
          </a>
        </div>
      )}
    </div>
  )
}
```

## Environment Variables

Make sure to set the following environment variables:

```bash theme={null}
NEXT_PUBLIC_DYNAMIC_ENVIRONMENT_ID=your_dynamic_environment_id
NEXT_PUBLIC_RHINESTONE_API_KEY=your_rhinestone_api_key
```

## Complete Example

Try the full integration in our example repository:

```bash theme={null}
git clone https://github.com/rhinestonewtf/e2e-examples.git
cd e2e-examples/dynamic
npm install && npm run dev
```

The example demonstrates:

* Dynamic wallet connection and authentication
* Rhinestone smart account creation
* Cross-chain transactions
* Error handling and best practices

## EIP-7702 Support

For [EIP-7702](/smart-wallet/core/eip-7702) with Dynamic, you need to create a custom viem account that wraps Dynamic's signer.

Get the Dynamic signer from the connector, then use viem's `toAccount` to create a compatible account:

```tsx theme={null}
import { useDynamicContext } from "@dynamic-labs/sdk-react-core";
import { toAccount } from "viem/accounts";

const { primaryWallet } = useDynamicContext();
const connector = primaryWallet.connector;
connector.setActiveAccount(primaryWallet.address);
const dynamicSigner = await connector.getSigner();

const eoaAccount = toAccount({
  address: primaryWallet.address,
  signMessage: ({ message }) => dynamicSigner.signMessage({ message }),
  signTransaction: (tx) => dynamicSigner.signTransaction(tx),
  signTypedData: (typedData) => dynamicSigner.signTypedData(typedData),
  signAuthorization: (authorization) => dynamicSigner.signAuthorization(authorization),
});
```

Pass this account to Rhinestone with the `eoa` parameter:

```tsx theme={null}
const rhinestoneAccount = await sdk.createAccount({
  owners: { type: "ecdsa", accounts: [eoaAccount] },
  eoa: eoaAccount,
});
```

## Next Steps

* **See it in action**: [Dynamic + Rhinestone Example](https://github.com/rhinestonewtf/e2e-examples/tree/main/dynamic)
* Learn more about [Dynamic's features](https://docs.dynamic.xyz/) for user onboarding
* Explore [chain abstraction](../../chain-abstraction/unified-balance) capabilities
* Check out [creating an account](../create-account) for more details
