> **Can't find what you're looking for?** Use `search_docs` on the docs MCP server at `https://www.openfort.io/api/mcp` to find what you need.
>
> **Have feedback?** Use `submit_feedback` on the same MCP server.

# Yield on Aave

Build a DeFi application that lets users supply USDC into Aave v4 lending pools and withdraw their yield — all through Openfort embedded wallets on Base.

:::tip
You'll build a web app where users authenticate with Openfort, view their USDC balance and Aave supply position with live APY, and supply or withdraw USDC from Aave — with optional gas sponsorship.
:::

![Aave recipe interface](https://www.openfort.io/images/blog/aave_f037ee07d7.png?updated_at=2025-09-19T12:00:10.064Z)

[View Sample Code](https://github.com/openfort-xyz/recipes-hub/tree/main/aave) — GitHub Repository. Complete source code for the Aave DeFi application.

## Build it with your agent

Adding this to an app you already have? Set up your coding agent with the [Openfort docs MCP server and skill](https://www.openfort.io/docs/overview/building-with-ai), then give it this prompt:

```text
Add USDC lending on Aave v4 (supply and withdraw) to this app using Openfort. Follow the "Add this to your app" section of
https://github.com/openfort-xyz/recipes-hub/blob/main/aave/AGENTS.md
```

The same file lists every Openfort primitive the recipe uses and the errors you're likely to hit, with their fixes.

## Getting started

::::steps
### Set up your project

Clone the recipe and install dependencies:

```bash
pnpx gitpick openfort-xyz/recipes-hub/tree/main/aave openfort-aave
cd openfort-aave
pnpm install
```

### Configure your Openfort credentials

This recipe uses **passkey** wallet recovery (client-side WebAuthn), so it needs no backend — only your Shield publishable key. Get your credentials from the [Openfort Dashboard](https://dashboard.openfort.io):

1. Navigate to **Developers** → **API Keys** and copy your **Publishable Key** (starts with `pk_`)
2. Copy your **Shield Publishable Key** from the Shield section of the same page
3. Optionally, go to **Gas sponsorship** → **Add gas sponsorship** → copy the **gas sponsorship ID**

Create the environment file and fill in your values:

```bash
cp .env.example .env
```

```bash
VITE_OPENFORT_PUBLISHABLE_KEY=pk_...
VITE_OPENFORT_SHIELD_PUBLISHABLE_KEY=...
VITE_OPENFORT_FEE_SPONSORSHIP_ID=pol_...     # Optional — enables gas sponsorship
VITE_WALLET_CONNECT_PROJECT_ID=...  # Optional — unset means no WalletConnect connector
```

:::tip
The app validates environment variables at startup. If any required variable is missing, you'll see an error modal explaining exactly what's needed.
:::

### Understand the provider setup

The app nests Wagmi, React Query, Aave, and Openfort providers. The Openfort SDK integrates as a wagmi connector, so all wallet interactions go through standard wagmi hooks:

```tsx
// src/Providers.tsx
const walletConnectProjectId = import.meta.env.VITE_WALLET_CONNECT_PROJECT_ID;
const connectors = [embeddedWalletConnector(), injected()];
if (walletConnectProjectId) connectors.push(walletConnect({ projectId: walletConnectProjectId }));

const wagmiConfig = createConfig({
  chains: [base, baseSepolia],
  connectors,
  transports: { [base.id]: http(), [baseSepolia.id]: http() },
});

<QueryClientProvider client={queryClient}>
  <WagmiProvider config={wagmiConfig}>
    <OpenfortWagmiBridge>
      <AaveProvider client={aaveClient}>
        <OpenfortProvider
          publishableKey={import.meta.env.VITE_OPENFORT_PUBLISHABLE_KEY}
          walletConfig={{
            shieldPublishableKey: import.meta.env.VITE_OPENFORT_SHIELD_PUBLISHABLE_KEY,
            ethereum: {
              ethereumFeeSponsorshipId: import.meta.env.VITE_OPENFORT_FEE_SPONSORSHIP_ID || undefined,
            },
          }}
          uiConfig={{
            walletRecovery: { defaultMethod: RecoveryMethod.PASSKEY },
          }}
        >
          {children}
        </OpenfortProvider>
      </AaveProvider>
    </OpenfortWagmiBridge>
  </WagmiProvider>
</QueryClientProvider>
```

The `embeddedWalletConnector` from `@openfort/react/wagmi` wires up the Openfort embedded wallet as a wagmi connector for Base and Base Sepolia. This means you use standard wagmi hooks like `useAccount()` and `useWalletClient()` — Openfort handles the signing behind the scenes.

### Explore the Aave supply flow

The core DeFi logic lives in the `useAaveOperations` hook. When a user clicks "Supply 0.1 USDC", the hook:

1. Passes an **execution-plan handler** to `useSupply` that routes each step of the plan to the wallet
2. Calls `supply()` keyed off the reserve id; the SDK figures out whether an approval (or permit) is needed first
3. Sends each step — approval/permit and the deposit — through the Openfort wallet

This recipe uses the Aave **v4** SDK (hub/spoke). The supply request takes a `reserve` id (no separate market/currency/chainId), and execution runs through a plan handler:

```typescript
// src/hooks/useAaveOperations.ts
const [supply] = useSupply((plan) => {
  switch (plan.__typename) {
    case "TransactionRequest":
      return sendTransaction(plan);
    case "Erc20Approval":
      // Approve Aave to spend USDC — by permit signature when supported, else a tx
      return plan.bySignature ? signTypedData(plan.bySignature) : sendTransaction(plan.byTransaction);
    case "PreContractActionRequired":
      return sendTransaction(plan.transaction);
  }
});

const result = await supply({
  reserve: usdcReserve.id,
  amount: { erc20: { value: bigDecimal(0.1) } }, // 0.1 USDC
  sender: evmAddress(walletClient.account.address),
});
```

The plan handler transparently covers both first-time users (who need an approval or permit) and returning users (who can supply directly). `sendTransaction`/`signTypedData` come from the `useSendTransaction`/`useSignTypedData` hooks in `@aave/react/viem`.

### Run the application

```bash
pnpm dev
```

Open [http://localhost:5173](http://localhost:5173). Click **Connect Wallet** to authenticate with Openfort, then:

1. Fund your embedded wallet with USDC on Base
2. Click **Supply 0.1 USDC to pool** to deposit into Aave
3. Watch your supply balance and APY update in real-time
4. Click **Withdraw all from pool** to redeem your USDC plus earned yield
::::

## How it works

The app reads on-chain data from two sources and writes transactions through the Openfort embedded wallet:

**USDC wallet balance** is read using wagmi's `useReadContract` to call `balanceOf` on the USDC contract for the connected chain (`0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913` on Base):

```typescript
// src/App.tsx
const { data: usdcBalance, refetch: refetchUsdcBalance } = useReadContract({
  address: usdcAddress,
  abi: usdcAbi,
  functionName: 'balanceOf',
  args: address ? [address] : undefined,
  query: { enabled: !!address && !!usdcAddress },
});
```

**Aave supply position and APY** are fetched with the `@aave/react` hooks: `useReserves` finds the USDC reserve and its supply APY, and `useUserSupplies` returns the user's supply positions:

```typescript
// src/hooks/useAaveSupplies.ts
const result = useUserSupplies({
  query: {
    userChains: {
      user,
      chainIds: currentChainId ? [toChainId(currentChainId)] : [],
    },
  },
  pause: !user || !currentChainId,
});
```

**Withdrawals** use the same plan pattern as supply but with `{ max: true }` to redeem the full position:

```typescript
// src/hooks/useAaveOperations.ts
const result = await withdraw({
  reserve: usdcReserve.id,
  amount: { erc20: { max: true } },
  sender: evmAddress(walletClient.account.address),
});
```
