> ## Documentation Index
> Fetch the complete documentation index at: https://www.dynamic.xyz/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Receiving USDC

> Surface your user's USDC receiving address — as a subdomain, QR code, raw address, or shareable payment link.

<Note>
  Examples use React with `@dynamic-labs-sdk/react-hooks`. The underlying JS SDK is framework-agnostic — swap the hooks for direct calls against `@dynamic-labs-sdk/client` to adapt this guide to vanilla JS, Vue, Svelte, or any other framework.
</Note>

The JavaScript SDK is headless, so receiving USDC always comes down to surfacing the user's wallet address in some shape your UI can render. This guide walks through four ways to do that.

## Setup

Follow the [React Quickstart (JS SDK)](/javascript/reference/react-quickstart) to scaffold a Vite + React app and wire `<DynamicProvider>` with the EVM extension.

## Subdomain (Global Identity)

If you enable [Global Identity](/react/wallets/embedded-wallets/mpc/global-identity) (configured in the dashboard, framework-agnostic), each EVM wallet gets a unique subdomain (e.g. `alice.dyn.eth`) that anyone can use to send funds. The subdomain lives on the wallet's verified credential as `nameService.name`:

```tsx theme={"system"}
import { useUser, useGetWalletAccounts } from "@dynamic-labs-sdk/react-hooks";
import { isEvmWalletAccount } from "@dynamic-labs-sdk/evm";

export function Subdomain() {
  const { data: user } = useUser();
  const { data: accounts = [] } = useGetWalletAccounts();
  const evmAccount = accounts.find(isEvmWalletAccount);

  const subdomain = user?.verifiedCredentials.find(
    (vc) => vc.address?.toLowerCase() === evmAccount?.address.toLowerCase()
  )?.nameService?.name;

  if (!subdomain) return null;
  return <p>Send USDC to: <strong>{subdomain}</strong></p>;
}
```

## QR code

Generate a QR for the raw address with any QR library (e.g. `qrcode.react`):

```tsx theme={"system"}
import { useGetWalletAccounts } from "@dynamic-labs-sdk/react-hooks";
import { isEvmWalletAccount } from "@dynamic-labs-sdk/evm";
import { QRCodeSVG } from "qrcode.react";

export function ReceiveQR() {
  const { data: accounts = [] } = useGetWalletAccounts();
  const address = accounts.find(isEvmWalletAccount)?.address;
  if (!address) return null;
  return <QRCodeSVG value={address} size={192} />;
}
```

## Wallet address

For copy-to-clipboard flows, read `address` straight off the wallet account:

```tsx theme={"system"}
import { useGetWalletAccounts } from "@dynamic-labs-sdk/react-hooks";
import { isEvmWalletAccount } from "@dynamic-labs-sdk/evm";

export function CopyAddress() {
  const { data: accounts = [] } = useGetWalletAccounts();
  const address = accounts.find(isEvmWalletAccount)?.address;
  if (!address) return null;
  return (
    <button onClick={() => navigator.clipboard.writeText(address)}>
      Copy {address.slice(0, 6)}…{address.slice(-4)}
    </button>
  );
}
```

## Payment links

A "payment link" is just a URL with the recipient, amount, and chain encoded as query params. The sender lands on a page in your app that reads the params and triggers a USDC `transfer` from their wallet.

### Generating the link

```tsx src/components/PaymentLinkGenerator.tsx theme={"system"}
import { useState } from "react";
import { useGetWalletAccounts } from "@dynamic-labs-sdk/react-hooks";
import { isEvmWalletAccount } from "@dynamic-labs-sdk/evm";
import { getActiveNetworkId } from "@dynamic-labs-sdk/client";

export function PaymentLinkGenerator() {
  const { data: accounts = [] } = useGetWalletAccounts();
  const walletAccount = accounts.find(isEvmWalletAccount);

  const [amount, setAmount] = useState("10");
  const [link, setLink] = useState("");

  const generate = async () => {
    if (!walletAccount) return;
    const { networkId } = await getActiveNetworkId({ walletAccount });
    const params = new URLSearchParams({
      recipient: walletAccount.address,
      amount,
      token: "USDC",
      chainId: networkId,
    });
    setLink(`${window.location.origin}/pay?${params}`);
  };

  return (
    <>
      <input type="number" value={amount} onChange={(e) => setAmount(e.target.value)} step="0.01" />
      <button onClick={generate} disabled={!walletAccount}>Generate link</button>
      {link && (
        <>
          <input readOnly value={link} />
          <button onClick={() => navigator.clipboard.writeText(link)}>Copy</button>
        </>
      )}
    </>
  );
}
```

`getActiveNetworkId` returns the wallet's current chain id (e.g. `"8453"` for Base) so the sender lands on the right chain. See [Getting Active Network](/javascript/reference/wallets/get-active-network).

### Processing the link

The payment page reads the URL params, switches the sender's wallet to the correct chain, then runs an ERC-20 `transfer`:

```tsx src/routes/Pay.tsx theme={"system"}
import { useState } from "react";
import { useUser, useGetWalletAccounts } from "@dynamic-labs-sdk/react-hooks";
import { isEvmWalletAccount } from "@dynamic-labs-sdk/evm";
import { createWalletClientForWalletAccount } from "@dynamic-labs-sdk/evm/viem";
import {
  getActiveNetworkId,
  switchActiveNetwork,
  NetworkNotAddedError,
  addNetwork,
} from "@dynamic-labs-sdk/client";
import { useSearchParams } from "react-router-dom";
import { parseUnits, erc20Abi } from "viem";

const USDC_BY_CHAIN: Record<string, `0x${string}`> = {
  "1": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // Ethereum
  "137": "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359", // Polygon (native)
  "42161": "0xaf88d065e77c8cC2239327C5EDb3A432268e5831", // Arbitrum (native)
  "10": "0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85", // Optimism (native)
  "8453": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", // Base
};

export function Pay() {
  const [params] = useSearchParams();
  const { data: user } = useUser();
  const { data: accounts = [] } = useGetWalletAccounts();
  const walletAccount = accounts.find(isEvmWalletAccount);

  const recipient = params.get("recipient") as `0x${string}` | null;
  const amount = params.get("amount");
  const targetChainId = params.get("chainId");

  const [pending, setPending] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const [hash, setHash] = useState<`0x${string}` | null>(null);

  if (!recipient || !amount || !targetChainId) return <p>Invalid payment link</p>;
  if (!user) return <p>Sign in to complete this payment</p>;

  const handlePay = async () => {
    if (!walletAccount) return setError("Connect an EVM wallet");
    const usdc = USDC_BY_CHAIN[targetChainId];
    if (!usdc) return setError(`USDC not supported on chain ${targetChainId}`);

    setError(null);
    setPending(true);
    try {
      const { networkId } = await getActiveNetworkId({ walletAccount });
      if (networkId !== targetChainId) {
        try {
          await switchActiveNetwork({ walletAccount, networkId: targetChainId });
        } catch (err) {
          if (err instanceof NetworkNotAddedError) {
            await addNetwork({ walletAccount, networkData: err.networkData });
            await switchActiveNetwork({ walletAccount, networkId: targetChainId });
          } else throw err;
        }
      }

      const walletClient = await createWalletClientForWalletAccount({ walletAccount });
      const tx = await walletClient.writeContract({
        address: usdc,
        abi: erc20Abi,
        functionName: "transfer",
        args: [recipient, parseUnits(amount, 6)],
      });
      setHash(tx);
    } catch (err: any) {
      setError(err.message ?? "Payment failed");
    } finally {
      setPending(false);
    }
  };

  if (hash) return <p>Paid! Tx: {hash}</p>;
  return (
    <>
      <p>Pay {amount} USDC to {recipient.slice(0, 8)}…{recipient.slice(-6)}</p>
      <button onClick={handlePay} disabled={pending}>{pending ? "Processing…" : "Pay"}</button>
      {error && <p style={{ color: "red" }}>{error}</p>}
    </>
  );
}
```

### Adding metadata

For richer payment requests (description, reference, expiry) just add more query params before generating the link:

```ts theme={"system"}
const params = new URLSearchParams({
  recipient: walletAccount.address,
  amount,
  token: "USDC",
  chainId: networkId,
  description: "Coffee",
  reference: "order-2026-0042",
  expiresAt: String(Date.now() + 60 * 60 * 1000), // 1 hour
});
```

Always validate `expiresAt` on the payment page before accepting the transfer.

### Security considerations

1. **Validate params server-side** if your app touches the recipient address or amount in any backend flow — the URL is user-controlled.
2. **Rate limit** the page that processes links so a leaked URL can't drain a wallet via auto-clicking.
3. **HTTPS only** — never serve payment URLs over plain HTTP.
4. **Bound amounts** — reject anything outside a sane min/max for your product.

## Related

* [Global Identity](/react/wallets/embedded-wallets/mpc/global-identity) — dashboard setup applies to any SDK
* [`getActiveNetworkId`](/javascript/reference/wallets/get-active-network)
* [`switchActiveNetwork`](/javascript/reference/wallets/switch-active-network)
* [Sending USDC](/recipes/stablecoins/sending-usdc)
* [Sending USDC by email or phone](/recipes/stablecoins/transfer-usdc-by-identifier)
