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

# Earn yield on stablecoins with Deframe Pods

> Learn how to integrate Deframe Pods yield strategies with Dynamic's embedded wallets for stablecoin yield earning

## What We're Building

A React (Next.js) app that connects Dynamic's embedded wallets to Deframe Pods yield strategies, allowing users to:

* Deposit stablecoins into yield strategies
* Withdraw from positions
* Track yields and positions across multiple protocols

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-stablecoin-yield-pods).

## Building the Application

### Project setup

Follow the [JS SDK Quickstart](/javascript/reference/quickstart) to initialize a Next.js app with Dynamic. Scaffold with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app), then continue with the dependencies below.

<Info>
  **Dashboard:** Under **Chains & Networks**, enable **Ethereum** and any other EVM networks you use. Under **Sign-in Methods** and **Wallets**, enable what your flow needs (including **Embedded wallets**). Under **Security** → **Allowed Origins**, add the origin where the app runs (for example `http://localhost:3000`).
</Info>

### Install Dependencies

<CodeGroup>
  ```bash npm theme={"system"}
  npm install @dynamic-labs-sdk/client @dynamic-labs-sdk/evm @dynamic-labs-sdk/react-hooks viem
  ```

  ```bash yarn theme={"system"}
  yarn add @dynamic-labs-sdk/client @dynamic-labs-sdk/evm @dynamic-labs-sdk/react-hooks viem
  ```

  ```bash pnpm theme={"system"}
  pnpm add @dynamic-labs-sdk/client @dynamic-labs-sdk/evm @dynamic-labs-sdk/react-hooks viem
  ```

  ```bash bun theme={"system"}
  bun add @dynamic-labs-sdk/client @dynamic-labs-sdk/evm @dynamic-labs-sdk/react-hooks viem
  ```
</CodeGroup>

### Configure Dynamic Environment

Create a `.env.local` file with your Dynamic environment ID and Deframe Pods API key:

```env .env.local theme={"system"}
NEXT_PUBLIC_DYNAMIC_ENV_ID=your-environment-id-here
NEXT_PUBLIC_PODS_API_KEY=your-pods-api-key-here
NEXT_PUBLIC_PODS_API_URL=https://api.deframe.io
```

You can find your Environment ID in the Dynamic dashboard under Developer Settings → SDK & API Keys.

For the Deframe Pods API key, visit [Deframe](https://deframe.io) to get your API key.

### Initialize Dynamic

Create `src/lib/dynamic.ts`:

```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: "Pods Yield" },
});

addEvmExtension();
```

### Configure Providers

Create `src/lib/providers.tsx`. `DynamicProvider` owns reactive auth state; `InnerProviders` reads it via `useUser()` and `useGetWalletAccounts()` from `@dynamic-labs-sdk/react-hooks`:

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

import {
  createContext, useContext, 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 { DynamicProvider, useUser, useGetWalletAccounts } from "@dynamic-labs-sdk/react-hooks";
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 } },
});

function InnerProviders({ children }: { children: ReactNode }) {
  const { data: user } = useUser();
  const loggedIn = user !== null;
  const { data: walletAccounts = [] } = useGetWalletAccounts();
  const evmAccount = walletAccounts.find(isEvmWalletAccount) ?? null;

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

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

  useEffect(() => {
    const unsub = onEvent(
      { event: "walletAccountsChanged", listener: () => { void ensureEvmWallet(); } },
      dynamicClient,
    );
    return () => unsub?.();
  }, [ensureEvmWallet]);

  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);
        }
      } catch {}
    };
    handleOAuthRedirect();
  }, [ensureEvmWallet]);

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

export default function Providers({ children }: { children: ReactNode }) {
  return (
    <DynamicProvider client={dynamicClient}>
      <InnerProviders>{children}</InnerProviders>
    </DynamicProvider>
  );
}
```

### Create Pods Client

Create `src/lib/pods.ts` for the Deframe Pods API client. This file contains all the API communication logic for interacting with the Deframe Pods service.

All type definitions are available in [`src/lib/pods-types.ts`](https://github.com/dynamic-labs/examples/blob/main/examples/nextjs-stablecoin-yield-pods/src/lib/pods-types.ts) in the GitHub repository.

```typescript src/lib/pods.ts theme={"system"}
const PODS_API_BASE = process.env.NEXT_PUBLIC_PODS_API_URL || "https://api.deframe.io";
const PODS_API_KEY = process.env.NEXT_PUBLIC_PODS_API_KEY;

async function fetchFromPodsAPI<T>(endpoint: string, options: RequestInit = {}): Promise<T> {
  const response = await fetch(`${PODS_API_BASE}${endpoint}`, {
    ...options,
    headers: {
      "Content-Type": "application/json",
      "x-api-key": PODS_API_KEY!,
      ...options.headers,
    },
  });

  if (!response.ok) {
    const error = await response.text();
    throw new Error(`Pods API error: ${error}`);
  }

  return response.json();
}

// getStrategies, getStrategy, getDepositBytecode, getWithdrawBytecode, getWalletPositions...

export const client = {
  getStrategies,
  getStrategy,
  getDepositBytecode,
  getWithdrawBytecode,
  getWalletPositions,
};
```

You can get detailed information about the Deframe Pods API [here](https://docs.deframe.io/api/get-strategies).

### Create Transaction Operations Hook

Create `src/lib/useTransactionOperations.ts` for handling deposits and withdrawals using `createWalletClientForWalletAccount` from the JS SDK:

```typescript src/lib/useTransactionOperations.ts theme={"system"}
import { useState, useCallback } from "react";
import { createWalletClientForWalletAccount } from "@dynamic-labs-sdk/evm/viem";
import { base, mainnet, polygon } from "viem/chains";
import { client as podsClient } from "./pods";
import type { Strategy } from "./pods-types";
import { useWallet } from "@/lib/providers";

function getViemChain(chainId: number) {
  switch (chainId) {
    case mainnet.id: return mainnet;
    case polygon.id: return polygon;
    default: return base;
  }
}

export function useTransactionOperations(_walletClient: unknown, selectedChainId: number) {
  const { evmAccount } = useWallet();
  const [isOperating, setIsOperating] = useState(false);
  const [operationError, setOperationError] = useState<Error | null>(null);

  const executeDeposit = useCallback(async (strategy: Strategy, amount: string) => {
    const walletAddress = evmAccount?.address;
    if (!walletAddress || !evmAccount) throw new Error("Wallet not connected");

    setIsOperating(true);
    try {
      const amountInSmallestUnit = BigInt(
        Math.floor(parseFloat(amount) * 10 ** strategy.assetDecimals)
      ).toString();

      const { bytecode } = await podsClient.getDepositBytecode({
        strategyId: strategy.id,
        chainId: selectedChainId,
        amount: amountInSmallestUnit,
        asset: strategy.assetName,
        wallet: walletAddress,
      });

      const chain = getViemChain(selectedChainId);
      const walletClient = createWalletClientForWalletAccount({ walletAccount: evmAccount, chain });

      let lastHash: string | undefined;
      for (const tx of bytecode) {
        const hash = await walletClient.sendTransaction({
          chain,
          account: evmAccount.address as `0x${string}`,
          to: tx.to as `0x${string}`,
          value: BigInt(tx.value),
          data: tx.data as `0x${string}`,
        });
        lastHash = hash;
      }
      return lastHash!;
    } catch (error) {
      const err = error instanceof Error ? error : new Error(String(error));
      setOperationError(err);
      throw err;
    } finally {
      setIsOperating(false);
    }
  }, [evmAccount, selectedChainId]);

  // executeWithdraw follows the same pattern...

  return { isOperating, operationError, executeDeposit /*, executeWithdraw */ };
}
```

### Build Yield Interface Component

Create `src/components/YieldInterface.tsx` for the main UI. It reads `evmAccount` from `useWallet()` instead of `useDynamicContext()`:

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

import { useEffect, useState, useCallback } from "react";
import { base } from "viem/chains";
import { useTransactionOperations } from "../lib/useTransactionOperations";
import { client as podsClient } from "../lib/pods";
import { useWallet } from "@/lib/providers";
import type { Strategy, WalletPositions } from "../lib/pods-types";

export function YieldInterface() {
  const { evmAccount } = useWallet();
  const [selectedChainId, setSelectedChainId] = useState(base.id);
  const [strategies, setStrategies] = useState<Strategy[]>([]);
  const [positions, setPositions] = useState<WalletPositions | null>(null);

  const { isOperating, executeDeposit, executeWithdraw } =
    useTransactionOperations(null, selectedChainId);

  const fetchStrategies = useCallback(async () => {
    const response = await podsClient.getStrategies(selectedChainId, 100);
    setStrategies(response.data.filter((s) => s.isActive !== false));
  }, [selectedChainId]);

  useEffect(() => { fetchStrategies(); }, [fetchStrategies]);

  useEffect(() => {
    if (!evmAccount?.address) { setPositions(null); return; }
    podsClient.getWalletPositions(evmAccount.address).then(setPositions).catch(() => setPositions(null));
  }, [evmAccount?.address]);

  // render strategies and positions...
}
```

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

### Configure CORS

Add your local development URL to the CORS origins in your Dynamic dashboard under Developer Settings > CORS Origins.

## Full source code

[GitHub repository →](https://github.com/dynamic-labs/examples/tree/main/examples/nextjs-stablecoin-yield-pods)

### Additional Resources

* [Earn yield with Aave](/recipes/integrations/yield/aave)
* [Earn yield with Morpho](/recipes/integrations/yield/morpho)
* [Dynamic JS SDK](/javascript/reference/quickstart)
* [Deframe Documentation](https://docs.deframe.io)
