Send money anywhere. Stablecoin rails underneath.
Email, social, or your own auth - senders sign in the way they already do, and an embedded MPC wallet appears behind the scenes, funded in USDC. Recipients get local currency over their local payment rails. And because users custody their own funds, expanding into new regions doesn't start with a licensing project. Built on Dynamic + Fireblocks.
@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-page.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 });An embedded wallet, gas paid for
DocsSigning in mints a non-custodial embedded (WaaS) wallet - no extension, no seed phrase; keys never leave Dynamic's MPC. Every payout below rides that same wallet with EIP-7702 gas sponsorship: flip the dashboard toggle and call one function - the app pays the network fee, the sender never touches ETH.
lib/transactions/send-usdc-transaction.tsimport { createWaasWalletAccounts } from "@dynamic-labs-sdk/client/waas"; import { sendSponsoredTransaction } from "@dynamic-labs-sdk/evm"; import { encodeFunctionData, erc20Abi, parseUnits } from "viem"; import { USDC_CONTRACT_ADDRESS, USDC_DECIMALS } from "@/lib/constants"; await createWaasWalletAccounts({ chains: ["EVM"] }); const { transactionHash } = await sendSponsoredTransaction({ walletAccount, calls: [ { target: USDC_CONTRACT_ADDRESS, data: encodeFunctionData({ abi: erc20Abi, functionName: "transfer", args: [recipient, parseUnits(amount, USDC_DECIMALS)], }), value: 0n, }, ], });Balances + transfer history
DocsBalances and transfer history each come from one hook - the wallet page renders the asset list and recent-activity feed straight from these queries, refetched as the wallet changes. This demo also runs a server-side Alchemy proxy for networks the hosted balances backend doesn't cover yet - same rule either way: the API key stays server-side, the client only ever calls your own route.
components/screens/tx-history-screen.tsximport { useGetTokenBalances, useGetTransactionHistory, } from "@dynamic-labs-sdk/react-hooks"; const { data: balances } = useGetTokenBalances({ walletAccount, includeNative: true }); const { data: history } = useGetTransactionHistory({ address: walletAccount.address, chain: "EVM", networkId, limit: 10, }); // history.transactions renders the list; pass history.nextOffset back to page.