> ## 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.

# Build a prediction market trading app with Polymarket

> Learn how to integrate Polymarket prediction markets with Dynamic's embedded wallets for seamless trading

## What We're Building

A React (Next.js) app that connects Dynamic's embedded wallets to Polymarket prediction markets, allowing users to:

* Browse active prediction markets with real-time prices
* Buy Yes/No outcome shares on markets
* View and manage portfolio positions
* Sell positions and redeem winning outcomes
* Deposit funds via multiple methods (QR, credit card, cross-chain)

If you want to take a quick look at the final code, check out the [GitHub repository](https://github.com/dynamic-labs/examples/tree/main/examples/nextjs-polymarket-demo).

<Frame>
  <iframe src="https://www.loom.com/embed/a253dfc963f04b8e8a345f6a89ae9433" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen width="100%" height="640" />
</Frame>

## Key Components

* **Dynamic JS SDK Embedded Wallets** - Non-custodial wallets with seamless auth
* **Polymarket CLOB Client** - Trading SDK for placing orders on Polymarket
* **Polygon Network** - All trades execute on Polygon mainnet

## Building the Application

### Project setup

Scaffold a Next.js project with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app). This recipe uses the Dynamic JS SDK (`@dynamic-labs-sdk/client` + `@dynamic-labs-sdk/evm`) for authentication and embedded wallet management, with viem for EVM interactions.

<Info>
  **Dashboard:** Under **Chains & Networks**, enable **Polygon**. Under **Sign-in Methods** and **Wallets**, enable what your flow needs (including **Embedded wallets**). Under **Security** → **Allowed Origins**, add your origin (for example `http://localhost:3000`).
</Info>

### Install Dependencies

Add the Dynamic JS SDK and Polymarket dependencies:

<CodeGroup>
  ```bash npm theme={"system"}
  npm install @dynamic-labs-sdk/client @dynamic-labs-sdk/evm @polymarket/clob-client ethers@^5.8.0 @tanstack/react-query viem lucide-react
  ```

  ```bash yarn theme={"system"}
  yarn add @dynamic-labs-sdk/client @dynamic-labs-sdk/evm @polymarket/clob-client ethers@^5.8.0 @tanstack/react-query viem lucide-react
  ```

  ```bash pnpm theme={"system"}
  pnpm add @dynamic-labs-sdk/client @dynamic-labs-sdk/evm @polymarket/clob-client ethers@^5.8.0 @tanstack/react-query viem lucide-react
  ```

  ```bash bun theme={"system"}
  bun add @dynamic-labs-sdk/client @dynamic-labs-sdk/evm @polymarket/clob-client ethers@^5.8.0 @tanstack/react-query viem lucide-react
  ```
</CodeGroup>

### Configure Environment

Create a `.env.local` file with your Dynamic environment ID:

```env .env.local theme={"system"}
NEXT_PUBLIC_DYNAMIC_ENV_ID=your-environment-id-here

# Optional: LiFi API key for higher rate limits on cross-chain deposits
NEXT_PUBLIC_LIFI_API_KEY=
```

### Initialize Dynamic Client

Create `src/lib/dynamic.ts` to initialize the Dynamic client with the EVM extension:

```typescript src/lib/dynamic.ts theme={"system"}
import { createDynamicClient } from "@dynamic-labs-sdk/client";
import { addEvmExtension } from "@dynamic-labs-sdk/evm";

export const dynamicClient = createDynamicClient({
  environmentId: process.env.NEXT_PUBLIC_DYNAMIC_ENV_ID!,
  metadata: {
    name: "Polymarket Demo",
  },
});

addEvmExtension();
```

### Configure Providers

Set up a wallet context using the JS SDK. Create `src/lib/providers.tsx`:

```typescript src/lib/providers.tsx theme={"system"}
"use client";

import {
  createContext,
  useContext,
  useState,
  useEffect,
  useCallback,
  type ReactNode,
} from "react";
import {
  getWalletAccounts,
  onEvent,
  isSignedIn,
  logout,
  detectOAuthRedirect,
  completeSocialAuthentication,
} from "@dynamic-labs-sdk/client";
import { createWaasWalletAccounts } from "@dynamic-labs-sdk/client/waas";
import {
  isEvmWalletAccount,
  type EvmWalletAccount,
} from "@dynamic-labs-sdk/evm";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { dynamicClient } from "./dynamic";

interface WalletContextValue {
  evmAccount: EvmWalletAccount | null;
  loggedIn: boolean;
  ensureEvmWallet: () => Promise<void>;
  disconnect: () => Promise<void>;
}

const WalletContext = createContext<WalletContextValue>({
  evmAccount: null,
  loggedIn: false,
  ensureEvmWallet: async () => {},
  disconnect: async () => {},
});

export function useWallet() {
  return useContext(WalletContext);
}

const queryClient = new QueryClient({
  defaultOptions: {
    queries: { staleTime: 1000 * 60 * 5, refetchOnWindowFocus: false },
  },
});

export default function Providers({ children }: { children: ReactNode }) {
  const [evmAccount, setEvmAccount] = useState<EvmWalletAccount | null>(null);
  const [loggedIn, setLoggedIn] = useState(false);

  const refresh = useCallback(() => {
    const accounts = getWalletAccounts(dynamicClient);
    setEvmAccount(accounts.find(isEvmWalletAccount) ?? null);
    setLoggedIn(isSignedIn(dynamicClient));
  }, []);

  const disconnect = useCallback(async () => {
    await logout(dynamicClient);
    setEvmAccount(null);
    setLoggedIn(false);
  }, []);

  const ensureEvmWallet = useCallback(async () => {
    try {
      const accounts = getWalletAccounts(dynamicClient);
      if (!accounts.some(isEvmWalletAccount) && isSignedIn(dynamicClient)) {
        await createWaasWalletAccounts({ chains: ["EVM"] }, dynamicClient);
      }
    } catch {
      // wallet may already exist — ignore
    }
    refresh();
  }, [refresh]);

  useEffect(() => {
    const handleOAuthRedirect = async () => {
      if (typeof window === "undefined") return;
      try {
        const url = new URL(window.location.href);
        if (await detectOAuthRedirect({ url }, dynamicClient)) {
          await completeSocialAuthentication({ url }, dynamicClient);
          await ensureEvmWallet();
          window.history.replaceState({}, "", window.location.pathname);
          return;
        }
      } catch {}
      refresh();
    };

    handleOAuthRedirect();

    const unsub1 = onEvent(
      { event: "walletAccountsChanged", listener: () => ensureEvmWallet() },
      dynamicClient
    );
    const unsub2 = onEvent(
      {
        event: "logout",
        listener: () => { setEvmAccount(null); setLoggedIn(false); },
      },
      dynamicClient
    );

    return () => { unsub1(); unsub2(); };
  }, [refresh, ensureEvmWallet]);

  return (
    <WalletContext.Provider value={{ evmAccount, loggedIn, ensureEvmWallet, disconnect }}>
      <QueryClientProvider client={queryClient}>
        {children}
      </QueryClientProvider>
    </WalletContext.Provider>
  );
}
```

### Build the Auth Button

Create a custom `DynamicButton` component at `src/components/dynamic/DynamicButton.tsx`. This handles Google OAuth, email OTP, and external EVM wallet connection:

```typescript src/components/dynamic/DynamicButton.tsx theme={"system"}
"use client";

import { useState, useCallback, useRef, useEffect } from "react";
import {
  getAvailableWalletProvidersData,
  connectAndVerifyWithWalletProvider,
  sendEmailOTP,
  verifyOTP,
  authenticateWithSocial,
  type OTPVerification,
} from "@dynamic-labs-sdk/client";
import { exportWaasPrivateKey } from "@dynamic-labs-sdk/client/waas";
import { useWallet } from "@/lib/providers";
import { dynamicClient } from "@/lib/dynamic";

export default function DynamicButton() {
  const { evmAccount, loggedIn, disconnect, ensureEvmWallet } = useWallet();
  const [open, setOpen] = useState(false);
  const [view, setView] = useState<"menu" | "email" | "otp" | "wallet">("menu");
  const [email, setEmail] = useState("");
  const [otp, setOtp] = useState("");
  const [otpVerification, setOtpVerification] = useState<OTPVerification | null>(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const panelRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    if (!open) return;
    function handler(e: MouseEvent) {
      if (panelRef.current && !panelRef.current.contains(e.target as Node)) {
        setOpen(false);
        setView("menu");
        setError(null);
      }
    }
    document.addEventListener("mousedown", handler);
    return () => document.removeEventListener("mousedown", handler);
  }, [open]);

  const handleSendOTP = useCallback(async (e: React.FormEvent) => {
    e.preventDefault();
    setLoading(true);
    setError(null);
    try {
      const verification = await sendEmailOTP({ email }, dynamicClient);
      setOtpVerification(verification);
      setView("otp");
    } catch (err) {
      setError(err instanceof Error ? err.message : "Failed to send OTP");
    } finally {
      setLoading(false);
    }
  }, [email]);

  const handleVerifyOTP = useCallback(async (e: React.FormEvent) => {
    e.preventDefault();
    if (!otpVerification) return;
    setLoading(true);
    setError(null);
    try {
      await verifyOTP({ otpVerification, verificationToken: otp }, dynamicClient);
      await ensureEvmWallet();
      setOpen(false);
      setView("menu");
    } catch (err) {
      setError(err instanceof Error ? err.message : "Invalid code");
    } finally {
      setLoading(false);
    }
  }, [otpVerification, otp, ensureEvmWallet]);

  const handleGoogle = useCallback(async () => {
    setLoading(true);
    setError(null);
    try {
      await authenticateWithSocial(
        { provider: "google", redirectUrl: window.location.href },
        dynamicClient
      );
    } catch (err) {
      setError(err instanceof Error ? err.message : "Google sign-in failed");
      setLoading(false);
    }
  }, []);

  const getEvmProviders = () =>
    getAvailableWalletProvidersData(dynamicClient).filter((p) => p.chain === "EVM");

  const handleConnectWallet = useCallback(async (providerKey: string) => {
    setLoading(true);
    setError(null);
    try {
      await connectAndVerifyWithWalletProvider({ walletProviderKey: providerKey }, dynamicClient);
      await ensureEvmWallet();
      setOpen(false);
      setView("menu");
    } catch (err) {
      setError(err instanceof Error ? err.message : "Connection failed");
    } finally {
      setLoading(false);
    }
  }, [ensureEvmWallet]);

  if (loggedIn && evmAccount) {
    return (
      <div className="relative" ref={panelRef}>
        <button
          onClick={() => setOpen((o) => !o)}
          className="flex items-center gap-2 px-3 py-2 rounded-lg border border-[#DADADA] bg-white hover:bg-[#F9F9F9] text-sm text-[#030303]"
        >
          <span className="w-6 h-6 rounded-full bg-[#4779FF] flex items-center justify-center text-white text-xs font-semibold">
            {evmAccount.address[0].toUpperCase()}
          </span>
          <span className="hidden sm:block font-mono text-xs text-[#606060]">
            {evmAccount.address.slice(0, 6)}...{evmAccount.address.slice(-4)}
          </span>
        </button>
        {open && (
          <div className="absolute right-0 top-full mt-2 w-52 bg-white rounded-xl border border-[#DADADA] shadow-lg p-2 z-50">
            <div className="px-3 py-2 mb-1">
              <p className="text-xs text-[#606060]">Connected</p>
              <p className="text-xs font-mono text-[#030303] truncate">{evmAccount.address}</p>
            </div>
            <hr className="border-[#DADADA] my-1" />
            <button
              onClick={() => { disconnect(); setOpen(false); }}
              className="w-full text-left px-3 py-2 text-sm text-red-600 hover:bg-red-50 rounded-lg"
            >
              Disconnect
            </button>
          </div>
        )}
      </div>
    );
  }

  return (
    <div className="relative" ref={panelRef}>
      <button
        onClick={() => { setOpen((o) => !o); setView("menu"); setError(null); }}
        className="px-4 py-2 rounded-lg text-sm font-medium bg-[#4779FF] text-white hover:bg-[#3366ee]"
      >
        Sign in
      </button>
      {open && (
        <div className="absolute right-0 top-full mt-2 w-72 bg-white rounded-xl border border-[#DADADA] shadow-lg p-4 z-50">
          {view === "menu" && (
            <div className="space-y-2">
              <p className="text-sm font-medium text-[#030303] mb-3">Sign in to Polymarket</p>
              <button onClick={handleGoogle} disabled={loading}
                className="w-full flex items-center gap-3 px-4 py-2.5 rounded-lg text-sm border border-[#DADADA] text-[#030303] hover:bg-[#F9F9F9]">
                Continue with Google
              </button>
              <button onClick={() => { setView("email"); setError(null); }}
                className="w-full flex items-center gap-3 px-4 py-2.5 rounded-lg text-sm border border-[#DADADA] text-[#030303] hover:bg-[#F9F9F9]">
                Continue with Email
              </button>
              {getEvmProviders().length > 0 && (
                <button onClick={() => { setView("wallet"); setError(null); }}
                  className="w-full flex items-center gap-3 px-4 py-2.5 rounded-lg text-sm border border-[#DADADA] text-[#030303] hover:bg-[#F9F9F9]">
                  Connect EVM Wallet
                </button>
              )}
            </div>
          )}
          {view === "email" && (
            <form onSubmit={handleSendOTP} className="space-y-3">
              <input type="email" value={email} onChange={(e) => setEmail(e.target.value)}
                placeholder="you@example.com" required
                className="w-full px-3 py-2 text-sm border border-[#DADADA] rounded-lg text-[#030303]" />
              {error && <p className="text-xs text-red-500">{error}</p>}
              <button type="submit" disabled={loading}
                className="w-full px-4 py-2.5 rounded-lg text-sm font-medium bg-[#4779FF] text-white">
                {loading ? "Sending…" : "Send code"}
              </button>
            </form>
          )}
          {view === "otp" && (
            <form onSubmit={handleVerifyOTP} className="space-y-3">
              <p className="text-sm text-[#606060]">Sent to {email}</p>
              <input type="text" value={otp} onChange={(e) => setOtp(e.target.value)}
                placeholder="6-digit code" required maxLength={6}
                className="w-full px-3 py-2 text-sm border border-[#DADADA] rounded-lg text-[#030303] text-center tracking-widest" />
              {error && <p className="text-xs text-red-500">{error}</p>}
              <button type="submit" disabled={loading}
                className="w-full px-4 py-2.5 rounded-lg text-sm font-medium bg-[#4779FF] text-white">
                {loading ? "Verifying…" : "Verify"}
              </button>
            </form>
          )}
          {view === "wallet" && (
            <div className="space-y-2">
              <p className="text-sm font-medium text-[#030303] mb-3">Choose an EVM wallet</p>
              {getEvmProviders().map((provider) => (
                <button key={provider.key} onClick={() => handleConnectWallet(provider.key)}
                  disabled={loading}
                  className="w-full flex items-center gap-3 px-4 py-2.5 rounded-lg text-sm border border-[#DADADA] text-[#030303] hover:bg-[#F9F9F9]">
                  {provider.metadata.icon && (
                    // eslint-disable-next-line @next/next/no-img-element
                    <img src={provider.metadata.icon} alt={provider.metadata.displayName}
                      width={20} height={20} className="rounded-sm" />
                  )}
                  {provider.metadata.displayName}
                </button>
              ))}
            </div>
          )}
        </div>
      )}
    </div>
  );
}
```

### Create Trading Hook

Build the trading hook that interfaces with the Polymarket CLOB API using a viem wallet client. Create `src/lib/hooks/usePolymarketTrading.ts`:

```typescript src/lib/hooks/usePolymarketTrading.ts theme={"system"}
"use client";

import { useState, useCallback, useRef } from "react";
import { useWallet } from "@/lib/providers";
import { createWalletClientForWalletAccount } from "@dynamic-labs-sdk/evm/viem";
import { polygon } from "viem/chains";
import { ClobClient, Side, OrderType } from "@polymarket/clob-client";
import type { UserMarketOrder } from "@polymarket/clob-client";
import { Contract, providers, BigNumber } from "ethers";
import {
  POLYMARKET_CONTRACTS,
  POLYMARKET_USDC_SPENDERS,
  POLYMARKET_OUTCOME_TOKEN_SPENDERS,
  ERC20_APPROVAL_ABI,
  ERC1155_APPROVAL_ABI,
} from "@/lib/constants/contracts";

const SIGNATURE_TYPE_EOA = 0;
const CLOB_API_URL = "https://clob.polymarket.com";
const POLYGON_CHAIN_ID = 137;
const MAX_ALLOWANCE = BigNumber.from(
  "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
);

export interface UserApiCredentials {
  key: string;
  secret: string;
  passphrase: string;
}

export interface TradeParams {
  marketId: string;
  conditionId: string;
  tokenId: string;
  side: "yes" | "no";
  amount: number;
  isMarketOrder?: boolean;
  negRisk?: boolean;
}

export function usePolymarketTrading() {
  const { evmAccount } = useWallet();
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const credentialsRef = useRef<UserApiCredentials | null>(null);
  const signerRef = useRef<providers.JsonRpcSigner | null>(null);

  const address = evmAccount?.address;

  const getEthersSigner = useCallback(async (): Promise<providers.JsonRpcSigner | null> => {
    if (!evmAccount) return null;

    if (signerRef.current) return signerRef.current;

    try {
      const walletClient = createWalletClientForWalletAccount({
        walletAccount: evmAccount,
        chain: polygon,
      });
      const provider = new providers.Web3Provider(walletClient as any);
      signerRef.current = provider.getSigner();
      return signerRef.current;
    } catch {
      return null;
    }
  }, [evmAccount]);

  const initializeCredentials = useCallback(async (): Promise<UserApiCredentials | null> => {
    const ethersSigner = await getEthersSigner();
    if (!ethersSigner || !address) {
      setError("Wallet not connected");
      return null;
    }

    if (credentialsRef.current) return credentialsRef.current;

    setIsLoading(true);
    setError(null);

    try {
      const tempClient = new ClobClient(CLOB_API_URL, POLYGON_CHAIN_ID, ethersSigner as any);
      let credentials: UserApiCredentials | null = null;

      try {
        const derivedCreds = await tempClient.deriveApiKey();
        if (derivedCreds?.key) credentials = derivedCreds;
      } catch {}

      if (!credentials) {
        const newCreds = await tempClient.createApiKey();
        if (newCreds?.key) credentials = newCreds;
        else throw new Error("Failed to create API credentials");
      }

      credentialsRef.current = credentials;
      return credentials;
    } catch (err) {
      setError(err instanceof Error ? err.message : "Failed to initialize credentials");
      return null;
    } finally {
      setIsLoading(false);
    }
  }, [getEthersSigner, address]);

  const placeOrder = useCallback(async (
    params: TradeParams
  ): Promise<{ success: boolean; orderId?: string; error?: string }> => {
    if (!address) return { success: false, error: "Wallet not connected" };

    setIsLoading(true);
    setError(null);

    const ethersSigner = await getEthersSigner();
    if (!ethersSigner) {
      setIsLoading(false);
      return { success: false, error: "Failed to get wallet signer" };
    }

    const credentials = credentialsRef.current || (await initializeCredentials());
    if (!credentials) {
      setIsLoading(false);
      return { success: false, error: "Failed to initialize trading credentials" };
    }

    try {
      const client = new ClobClient(
        CLOB_API_URL, POLYGON_CHAIN_ID, ethersSigner as any, credentials, SIGNATURE_TYPE_EOA
      );

      const marketOrder: UserMarketOrder = {
        tokenID: params.tokenId,
        amount: params.amount,
        side: Side.BUY,
        feeRateBps: 0,
      };

      const response = await client.createAndPostMarketOrder(
        marketOrder,
        { negRisk: params.negRisk },
        OrderType.FOK
      );

      const orderId = response.orderID || response.orderId || response.id;
      setIsLoading(false);

      if (orderId) return { success: true, orderId };
      if (response.success) return { success: true, orderId: "pending" };
      throw new Error(response.error || "Order submission failed");
    } catch (err) {
      const errorMessage = err instanceof Error ? err.message : "Failed to place order";
      setError(errorMessage);
      setIsLoading(false);
      return { success: false, error: errorMessage };
    }
  }, [address, getEthersSigner, initializeCredentials]);

  return { placeOrder, initializeCredentials, isLoading, error };
}
```

The key difference from the React SDK version is using `createWalletClientForWalletAccount` from `@dynamic-labs-sdk/evm/viem` to get a viem wallet client from the `EvmWalletAccount`, which is then wrapped with ethers.js for compatibility with the Polymarket CLOB client.

### Create Markets API Route

Create an API route to fetch markets from Polymarket. Create `src/app/api/polymarket/route.ts`:

```typescript src/app/api/polymarket/route.ts theme={"system"}
import { NextResponse } from "next/server";

export async function GET(request: Request) {
  try {
    const { searchParams } = new URL(request.url);
    const limit = searchParams.get("limit") || "100";

    const url = new URL("https://gamma-api.polymarket.com/markets");
    url.searchParams.set("limit", limit);
    url.searchParams.set("closed", "false");
    url.searchParams.set("active", "true");

    const response = await fetch(url.toString(), {
      headers: { Accept: "application/json" },
      next: { revalidate: 60 },
    });

    if (!response.ok) throw new Error(`Polymarket API error: ${response.status}`);

    const data = await response.json();
    return NextResponse.json(data);
  } catch (error) {
    return NextResponse.json(
      { error: "Failed to fetch markets" },
      { status: 500 }
    );
  }
}
```

### Build the Main Page

Update `src/app/page.tsx` to display markets using the hooks:

```typescript src/app/page.tsx theme={"system"}
"use client";

import { useMemo, useState } from "react";
import { Header } from "@/components/Header";
import { MarketCard } from "@/components/MarketCard";
import {
  usePolymarketMarkets,
  type Market,
  calculateTimeRemaining,
} from "@/lib/hooks/usePolymarketMarkets";

export default function Home() {
  const [searchQuery, setSearchQuery] = useState("");
  const { data: markets = [], isLoading, error } = usePolymarketMarkets();

  const filteredMarkets = useMemo(() => {
    const lowerQuery = searchQuery.toLowerCase();
    return markets.filter((m: Market) =>
      !lowerQuery || m.question.toLowerCase().includes(lowerQuery)
    );
  }, [markets, searchQuery]);

  return (
    <>
      <Header searchValue={searchQuery} onSearchChange={setSearchQuery} />

      <div className="pt-[27px] pb-[93px]">
        {isLoading ? (
          <div className="text-center py-20">
            <p className="text-[#606060]">Loading markets...</p>
          </div>
        ) : error ? (
          <div className="text-center py-20">
            <p className="text-[#606060]">Error loading markets. Please try again later.</p>
          </div>
        ) : filteredMarkets.length > 0 ? (
          <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-5">
            {filteredMarkets.map((market: Market) => (
              <MarketCard
                key={market.id}
                question={market.question}
                timeRemaining={calculateTimeRemaining(market.endDate)}
                yesPrice={market.yesPrice}
                noPrice={market.noPrice}
                conditionId={market.conditionId}
                yesTokenId={market.yesTokenId}
                noTokenId={market.noTokenId}
                marketId={market.id}
                tags={market.tags}
              />
            ))}
          </div>
        ) : (
          <div className="text-center py-20">
            <p className="text-[#606060]">No markets found</p>
          </div>
        )}
      </div>
    </>
  );
}
```

### Run the Application

Start the development server:

<CodeGroup>
  ```bash npm theme={"system"}
  npm run dev
  ```

  ```bash yarn theme={"system"}
  yarn dev
  ```

  ```bash pnpm theme={"system"}
  pnpm dev
  ```

  ```bash bun theme={"system"}
  bun dev
  ```
</CodeGroup>

The application will be available at `http://localhost:3000`.

## How Trading Works

When a user places a trade on Polymarket through this app:

1. **Authentication**: The user signs in via Google, email OTP, or external EVM wallet using the custom `DynamicButton`
2. **Wallet Provisioning**: After auth, an EVM embedded wallet is automatically created via `createWaasWalletAccounts`
3. **Wallet Client**: `createWalletClientForWalletAccount` from `@dynamic-labs-sdk/evm/viem` creates a viem-compatible wallet client
4. **Credential Initialization**: The app derives or creates Polymarket API credentials by signing a message
5. **Approvals**: The app checks and requests necessary ERC20/ERC1155 approvals for USDC and outcome tokens
6. **Order Placement**: A market order is submitted to Polymarket's CLOB API
7. **Confirmation**: The user sees success/error feedback

## Conclusion

If you want to take a look at the full source code, check out the [GitHub repository](https://github.com/dynamic-labs/examples/tree/main/examples/nextjs-polymarket-demo).

This integration demonstrates how Dynamic's JS SDK embedded wallets can seamlessly connect to prediction markets like Polymarket. The `@dynamic-labs-sdk/evm` package provides the `createWalletClientForWalletAccount` helper to bridge embedded wallets with viem, enabling complex trading operations with minimal friction.

### Additional Resources

* [Dynamic JS SDK Documentation](https://www.dynamic.xyz/docs/javascript/)
* [Polymarket CLOB API Documentation](https://docs.polymarket.com/)
* [Polygon Network Documentation](https://polygon.technology/developers)
