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-hooks
  1. Create the client

    Docs

    One Dynamic client per app, created at module scope. The EVM extension registers the networks the embedded wallet supports.

    lib/dynamic/client.ts
    import { 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);
  2. Wrap your app for hooks

    Docs

    The 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.tsx
    import { 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>
      );
    }
  3. Sign in with an email code

    Docs

    The 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.tsx
    import { 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 });
  4. An embedded wallet, gas paid for

    Docs

    Signing 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.ts
    import { 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,
        },
      ],
    });
  5. Balances + transfer history

    Docs

    Balances 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.tsx
    import {
      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.
Remittance - Dynamic Demos