Prerequisites
- A React or Next.js app set up with Dynamic’s React SDK
- Installed dependencies:
@coral-xyz/anchor
@solana/web3.js
@dynamic-labs/sdk-react-core
@dynamic-labs/solana
- Your Anchor program’s IDL and TypeScript types
Step 1: Create the Anchor Provider Context
Create a file (e.g., AnchorProviderComponent.tsx
) and add the following code. This context will provide your Anchor program, connection, provider, and public key throughout your app.
"use client";
import { AnchorProvider, Program } from "@coral-xyz/anchor";
import { useDynamicContext } from "@dynamic-labs/sdk-react-core";
import { isSolanaWallet } from "@dynamic-labs/solana";
import { Connection, PublicKey } from "@solana/web3.js";
import {
createContext,
ReactNode,
useCallback,
useContext,
useEffect,
useMemo,
useState,
} from "react";
// Import your program's types and IDL
import { YourProgram } from './path-to-your-program-types';
import yourProgramIdl from './path-to-your-idl.json';
interface AnchorProviderContextValue {
program: Program<YourProgram> | null;
connection: Connection | null;
provider: AnchorProvider | null;
publicKey: PublicKey | null;
}
const AnchorProviderContext = createContext<AnchorProviderContextValue>({
program: null,
connection: null,
provider: null,
publicKey: null,
});
export function useAnchorProvider() {
return useContext(AnchorProviderContext);
}
interface AnchorProviderProps {
children: ReactNode;
}
export default function AnchorProviderComponent({ children }: AnchorProviderProps) {
const { primaryWallet, sdkHasLoaded } = useDynamicContext();
const [connection, setConnection] = useState<Connection | null>(null);
const [provider, setProvider] = useState<AnchorProvider | null>(null);
const [program, setProgram] = useState<Program<YourProgram> | null>(null);
// Derive the public key from the connected wallet
const publicKey = useMemo(() => {
if (!primaryWallet?.address) return null;
try {
return new PublicKey(primaryWallet.address);
} catch (error) {
console.error("Invalid public key:", error);
return null;
}
}, [primaryWallet?.address]);
// Initialize Anchor when wallet or SDK state changes
const initAnchor = useCallback(async () => {
if (!primaryWallet || !sdkHasLoaded || !publicKey) {
setConnection(null);
setProvider(null);
setProgram(null);
return;
}
try {
if (!isSolanaWallet(primaryWallet)) {
console.error("Primary wallet is not a Solana wallet");
return;
}
const newConnection = await primaryWallet.getConnection();
const signer = await primaryWallet.getSigner();
const newProvider = new AnchorProvider(
newConnection,
{
publicKey,
signTransaction: async (tx) => signer.signTransaction(tx),
signAllTransactions: async (txs) => signer.signAllTransactions(txs),
},
{
commitment: "confirmed",
skipPreflight: true,
}
);
const newProgram = new Program<YourProgram>(yourProgramIdl, newProvider);
setConnection(newConnection);
setProvider(newProvider);
setProgram(newProgram);
} catch (error) {
console.error("Error initializing Anchor provider:", error);
setConnection(null);
setProvider(null);
setProgram(null);
}
}, [primaryWallet, sdkHasLoaded, publicKey]);
useEffect(() => {
initAnchor();
}, [initAnchor]);
const contextValue = useMemo(
() => ({
program,
connection,
provider,
publicKey,
}),
[program, connection, provider, publicKey]
);
return (
<AnchorProviderContext.Provider value={contextValue}>
{children}
</AnchorProviderContext.Provider>
);
}
Step 2: Wrap Your App with the Provider
Wrap your app with the AnchorProviderComponent
so all child components can access the Anchor context.
import AnchorProviderComponent from './AnchorProviderComponent';
function App({ children }) {
return (
<AnchorProviderComponent>
{children}
</AnchorProviderComponent>
);
}
Step 3: Use the Hook in Your Components
You can now access the Anchor context anywhere in your component tree. Here’s an example of calling a program method and sending a transaction using the Dynamic wallet:
import { useAnchorProvider } from './AnchorProviderComponent';
import { useDynamicContext } from "@dynamic-labs/sdk-react-core";
function MyComponent() {
const { program, connection, provider, publicKey } = useAnchorProvider();
const { primaryWallet } = useDynamicContext();
// Example: Call a program method and send a transaction
async function callYourMethod() {
if (!program || !publicKey || !primaryWallet) return;
// Build the transaction using Anchor
const tx = await program.methods
.yourMethodName()
.accounts({
// Add your required accounts here
user: publicKey,
// ... other accounts
})
.transaction();
// Get the signer from Dynamic
const signer = await primaryWallet.getSigner();
// Sign and send the transaction (type mismatch warning is safe to ignore)
const result = await signer.signAndSendTransaction(tx);
// Handle result as needed
}
return <div>Connected as: {publicKey?.toBase58() ?? 'Not connected'}</div>;
}
Summary
By following this guide, you can connect your Solana Anchor programs to your React or Next.js app using Dynamic for wallet management. This setup enables seamless smart contract interactions and transaction signing, leveraging the strengths of both Anchor and Dynamic.
References: