Trade everything. One app, every market.
Users scatter across apps because no single one offers every market. Give them an invisible embedded MPC wallet at login - no seed phrase, no extension - and put token markets, real-world events, and onchain swaps behind one familiar interface and one unified portfolio. Become the place where your users trade everything - built entirely on Dynamic.
@dynamic-labs-sdk/client@dynamic-labs-sdk/react-hooksCreate the client
DocsOne Dynamic client per app, created at module scope. The EVM extension registers the networks the embedded wallet supports.
lib/dynamic/client.tsimport { createDynamicClient } from "@dynamic-labs-sdk/client"; import { addEvmExtension } from "@dynamic-labs-sdk/evm"; export const client = createDynamicClient({ environmentId: process.env.NEXT_PUBLIC_DYNAMIC_ENVIRONMENT_ID!, }); addEvmExtension(client);Wrap your app for hooks
DocsThe react-hooks package reads the client from context and uses TanStack Query under the hood, so mount QueryClientProvider outside DynamicProvider once - every hook below works anywhere inside.
app/providers.tsximport { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { DynamicProvider } from "@dynamic-labs-sdk/react-hooks"; import { client } from "@/lib/dynamic/client"; const queryClient = new QueryClient(); export function Providers({ children }: { children: React.ReactNode }) { return ( <QueryClientProvider client={queryClient}> <DynamicProvider client={client}>{children}</DynamicProvider> </QueryClientProvider> ); }Sign in with an email code
DocsThe login card on the left runs this flow: send a one-time passcode, verify it. No password, no seed phrase - the session lives in the client.
components/login.tsximport { useSendEmailOTP, useVerifyOTP } from "@dynamic-labs-sdk/react-hooks"; const { mutate: sendOtp, data: otpVerification } = useSendEmailOTP(); const { mutate: verifyOtp } = useVerifyOTP(); sendOtp({ email }); // User types the 6-digit code from their inbox: verifyOtp({ otp: code, otpVerification });Sign in with a social account
DocsKick off the provider's OAuth flow, then complete it when the user lands back - same session, same embedded wallet.
components/social-login.tsximport { useCompleteSocialRedirect, useSignInWithSocialRedirect, } from "@dynamic-labs-sdk/react-hooks"; const { mutate: signIn } = useSignInWithSocialRedirect(); const { mutate: completeRedirect } = useCompleteSocialRedirect(); signIn({ provider: "google", redirectUrl: window.location.origin }); // ...and when the provider redirects back: completeRedirect({ url: new URL(window.location.href) });Create the embedded wallet
DocsOne call mints a non-custodial embedded (WaaS) wallet for the signed-in user - no extension, no seed phrase; keys never leave Dynamic's MPC. The hook lists every account and re-renders as they're created.
lib/dynamic/wallets.tsimport { createWaasWalletAccounts } from "@dynamic-labs-sdk/client/waas"; import { useGetWalletAccounts } from "@dynamic-labs-sdk/react-hooks"; await createWaasWalletAccounts({ chains: ["EVM"] }); const { data: walletAccounts } = useGetWalletAccounts(); const walletAccount = walletAccounts?.[0];Execute a swap
DocsWrap the wallet account in a viem-compatible client and send the router calldata your aggregator quoted - the user approves in place; keys never leave Dynamic's MPC.
lib/trade/execute-swap.tsimport { createWalletClientForWalletAccount } from "@dynamic-labs-sdk/evm/viem"; const walletClient = await createWalletClientForWalletAccount({ walletAccount }); // quote.transactionRequest comes from your swap aggregator's API. const hash = await walletClient.sendTransaction({ to: quote.transactionRequest.to, data: quote.transactionRequest.data, value: BigInt(quote.transactionRequest.value ?? 0), });Track balances and the portfolio
DocsRead token balances with one hook - the portfolio view renders straight from the query and stays fresh as trades settle.
components/portfolio.tsximport { useGetTokenBalances } from "@dynamic-labs-sdk/react-hooks"; const { data: balances } = useGetTokenBalances({ walletAccount });