> ## 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.

# Openfort

> Integrate Openfort signers with Rhinestone smart accounts.

## Overview

Openfort provides embedded wallet infrastructure that enables seamless user authentication and wallet management with built-in Shield security. This guide shows you how to integrate Openfort signers with Rhinestone smart accounts to create a secure, cross-chain wallet experience.

**How it works:** Openfort handles user authentication and embedded wallet creation with Shield security, 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

* An Openfort account
* Openfort publishable key
* Shield publishable key
* React application setup

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

    <CodeGroup>
      ```bash npm theme={null}
      npm install @openfort/react @tanstack/react-query @rhinestone/sdk viem wagmi
      ```

      ```bash pnpm theme={null}
      pnpm add @openfort/react @tanstack/react-query @rhinestone/sdk viem wagmi
      ```

      ```bash bun theme={null}
      bun install @openfort/react @tanstack/react-query @rhinestone/sdk viem wagmi
      ```
    </CodeGroup>
  </Step>

  <Step title="Set Up Openfort Provider">
    First, set up the Openfort provider with wagmi and React Query in your React application:

    ```tsx theme={null}
    import { OpenfortProvider, getDefaultConfig, AccountTypeEnum, AuthProvider } from '@openfort/react'
    import { WagmiProvider, createConfig } from 'wagmi'
    import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
    import { sepolia } from 'viem/chains'

    // Configure wagmi
    const wagmiConfig = createConfig(
      getDefaultConfig({
        appName: 'My App',
        chains: [sepolia],
        ssr: true
      })
    )

    // Create query client
    const queryClient = new QueryClient()

    function App() {
      return (
        <WagmiProvider config={wagmiConfig}>
          <QueryClientProvider client={queryClient}>
            <OpenfortProvider
              publishableKey={process.env.NEXT_PUBLIC_OPENFORT_PUBLISHABLE_KEY}
              walletConfig={{
                shieldPublishableKey: process.env.NEXT_PUBLIC_SHIELD_PUBLISHABLE_KEY,
                accountType: AccountTypeEnum.EOA
              }}
              uiConfig={{
                authProviders: [
                  AuthProvider.EMAIL,
                  AuthProvider.GOOGLE,
                  AuthProvider.GUEST
                ]
              }}
            >
              {/* Your app components */}
            </OpenfortProvider>
          </QueryClientProvider>
        </WagmiProvider>
      )
    }
    ```
  </Step>

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

    <Note>Openfort provides embedded wallets with Shield security that can be accessed via a viem-compatible provider.</Note>

    ```tsx theme={null}
    import { useState, useEffect, useCallback } from 'react'
    import { useUser, useWallets, useOpenfort } from '@openfort/react'
    import { createWalletClient, custom } from 'viem'
    import { sepolia } from 'viem/chains'
    import { RhinestoneSDK, walletClientToAccount } from '@rhinestone/sdk'

    interface OpenfortWalletState {
      rhinestoneAccount: any | null
      address: string | null
      isLoading: boolean
      error: string | null
      isAuthenticated: boolean
    }

    export function useOpenfortWallet(): OpenfortWalletState & {
      reconnect: () => Promise<void>
    } {
      const { isAuthenticated } = useUser()
      const { wallets } = useWallets()
      const { client } = useOpenfort()
      const [state, setState] = useState<OpenfortWalletState>({
        rhinestoneAccount: null,
        address: null,
        isLoading: false,
        error: null,
        isAuthenticated: false,
      })

      const initializeAccount = useCallback(async () => {
        if (!isAuthenticated || !wallets || wallets.length === 0) {
          setState(prev => ({
            ...prev,
            rhinestoneAccount: null,
            address: null,
            isAuthenticated: false,
            error: null,
          }))
          return
        }

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

        try {
          // Get Ethereum provider from Openfort's embedded wallet
          const ethereumProvider = await client.embeddedWallet.getEthereumProvider()

          // Get signer address from Openfort wallet
          const signerAddress = wallets[0].address as `0x${string}`

          // Create wallet client with viem
          const walletClient = createWalletClient({
            chain: sepolia,
            transport: custom(ethereumProvider),
            account: signerAddress
          })

          // Wrap wallet client 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 Openfort's wallet client
          const rhinestone = new RhinestoneSDK({
            apiKey,
          })
          const account = await rhinestone.createAccount({
            owners: {
              type: "ecdsa",
              accounts: [wrappedWalletClient],
            },
          })

          setState(prev => ({
            ...prev,
            rhinestoneAccount: account,
            address: signerAddress,
            isAuthenticated: 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,
            isAuthenticated: false,
          }))
        }
      }, [isAuthenticated, wallets, client])

      useEffect(() => {
        initializeAccount()
      }, [initializeAccount])

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

## Usage

### Basic Component Integration

Use the enhanced hook in your React components with Openfort's pre-built authentication UI:

```tsx theme={null}
import { useOpenfortWallet } from './hooks/useOpenfortWallet'
import { OpenfortButton } from '@openfort/react'

function WalletDashboard() {
  const {
    rhinestoneAccount,
    address,
    isLoading,
    error,
    isAuthenticated,
    reconnect
  } = useOpenfortWallet()

  // Handle loading state
  if (isLoading) {
    return (
      <div>
        <div></div>
        <span>Setting up your wallet...</span>
      </div>
    )
  }

  // Handle error state
  if (error) {
    return (
      <div>
        <h3>Wallet Setup Error</h3>
        <p>{error}</p>
        <button onClick={reconnect}>
          Try Again
        </button>
      </div>
    )
  }

  // Handle disconnected state - use OpenfortButton for auth
  if (!isAuthenticated || !rhinestoneAccount) {
    return (
      <div>
        <h2>
          Connect Your Wallet
        </h2>
        <p>
          Connect with Openfort to access cross-chain functionality
        </p>
        <OpenfortButton />
      </div>
    )
  }

  // Connected state
  return (
    <div>
      <h2>
        Wallet Connected
      </h2>
      <div>
        <p><strong>EOA Address:</strong> {address}</p>
        <p><strong>Smart Account:</strong> {rhinestoneAccount.getAddress()}</p>
        <p>Ready for cross-chain transactions!</p>
      </div>
      <div>
        <OpenfortButton />
      </div>
    </div>
  )
}
```

### Cross-Chain Transactions

Send transactions using the Openfort-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>
      <button
        onClick={handleCrossChainTransfer}
        disabled={isTransacting || !rhinestoneAccount}
      >
        {isTransacting ? 'Sending...' : 'Send 10 USDC (Base → Arbitrum)'}
      </button>

      {error && (
        <div>
          Error: {error}
        </div>
      )}

      {txResult && (
        <div>
          Transaction successful!
          <a
            href={`https://arbiscan.io/tx/${txResult.hash}`}
            target="_blank"
            rel="noopener noreferrer"
          >
            View on Arbiscan
          </a>
        </div>
      )}
    </div>
  )
}
```

## The Integration Pattern

All embedded signer integrations follow the same pattern:

1. **Provider Setup**: Configure the embedded wallet provider (Openfort, Dynamic, Privy, etc.)
2. **Get Wallet Client**: Use the provider's SDK to access the authenticated wallet and create a viem wallet client
3. **Pass to Rhinestone**: Create a Rhinestone account using the wallet client as a signer
4. **Use Cross-Chain Features**: The resulting account has all of Rhinestone's capabilities

This pattern means you can easily switch between providers or support multiple providers in the same app.

## Authentication Methods

Openfort supports multiple authentication methods out of the box:

* **Email**: Users can sign in with their email address
* **Social**: Google and other OAuth providers
* **Guest**: Anonymous authentication for quick onboarding

The `<OpenfortButton />` component automatically handles the authentication flow with all configured methods.

## Shield Security

Openfort's Shield provides additional security for embedded wallets:

* **Key Management**: Secure key storage and recovery
* **Transaction Protection**: Advanced fraud detection
* **Recovery Options**: User-friendly account recovery flows

Configure Shield in your `OpenfortProvider` with the `shieldPublishableKey`.

## Environment Variables

Make sure to set the following environment variables:

```bash theme={null}
NEXT_PUBLIC_OPENFORT_PUBLISHABLE_KEY=your_openfort_publishable_key
NEXT_PUBLIC_SHIELD_PUBLISHABLE_KEY=your_shield_publishable_key
NEXT_PUBLIC_RHINESTONE_API_KEY=your_rhinestone_api_key
```

## Next Steps

* Learn more about [Openfort's embedded wallets](https://www.openfort.io/docs/products/embedded-wallet/react)
* Explore [chain abstraction](../../chain-abstraction/single-chain-intent) capabilities
* Check out [creating an account](../create-account) for more details
