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.
Prerequisites
- A Dynamic account and project (with Ethereum enabled)
- React application setup
Installation
Install the required dependencies:
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:
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.
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:
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:
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:
NEXT_PUBLIC_DYNAMIC_ENVIRONMENT_ID=your_dynamic_environment_id
NEXT_PUBLIC_RHINESTONE_API_KEY=your_rhinestone_api_key
Next Steps