Yield
Earning yield on stablecoins and other assets through DeFi protocols like Aave provides users with opportunities to generate passive income while maintaining liquidity. With WaaP's 2PC and 2PC-MPC security, users can safely interact with lending protocols without compromising their private keys. This recipe demonstrates how to integrate Aave V3 lending markets with WaaP, allowing users to supply assets to earn interest and borrow against their collateral.
What are we cooking?
A React application based on WaaP quick start example with WAGMI that connects WaaP to Aave V3 lending markets, enabling users to:
- Supply assets to earn yield
- Borrow against supplied collateral
- Track positions and account health
- Manage deposits and withdrawals
Key Components
- WaaP - Non-custodial wallets with 2PC and 2PC-MPC security and policy engine
- Aave V3 Markets - Lending protocol with supply/borrow functionality
- Wagmi Integration - Type-safe blockchain interactions
- Transaction Simulation - Built-in transaction previews
Project Setup
Get started with WaaP quick start example
npx gitpick holonym-foundation/waap-examples/tree/main/waap-wagmi-nextjs
cd waap-wagmi-nextjs
npm install
npm run dev
Install Aave Dependencies
npm install @aave/react
The @aave/react
package provides hooks and utilities for interacting with Aave V3 markets. For more information, see the Aave React SDK documentation.
Configure Aave Client
Create src/lib/aave.ts
:
import { AaveClient } from "@aave/react";
export const aaveClient = AaveClient.create();
Setup Providers
Configure your app with the necessary providers in src/lib/providers.tsx
:
"use client";
import { WagmiProvider } from "wagmi";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { AaveProvider } from "@aave/react";
import { config } from "./wagmi";
import { aaveClient } from "./aave";
export default function Providers({ children }: { children: React.ReactNode }) {
const queryClient = new QueryClient();
return (
<WagmiProvider config={config}>
<AaveProvider client={aaveClient}>
<QueryClientProvider client={queryClient}>
{children}
</QueryClientProvider>
</AaveProvider>
</WagmiProvider>
);
}
Core Functionality
Supply Assets to Earn Yield
Supply stablecoins or other assets to Aave markets to earn interest:
import { useState } from 'react';
import { useAccount, useWriteContract, useWaitForTransactionReceipt } from 'wagmi';
import { parseUnits } from 'viem';
import { supply, evmAddress, chainId } from '@aave/react';
function SupplyAsset() {
const { address } = useAccount();
const [amount, setAmount] = useState('');
const [isSupplying, setIsSupplying] = useState(false);
// Aave V3 USDC market on Sepolia testnet
const marketAddress = '0x6Ae43d3271ff6888e7Fc43Fd7321a503ff738951';
const usdcAddress = '0x1c7D4B196Cb0C7B01d743Fbc70B6A3cE8C4C4C4C4';
const { writeContract, data: hash } = useWriteContract();
const { isLoading: isConfirming, isSuccess } = useWaitForTransactionReceipt({
hash,
});
const handleSupply = async () => {
if (!amount || parseFloat(amount) <= 0) return;
setIsSupplying(true);
try {
// First, approve USDC spending
await writeContract({
address: usdcAddress,
abi: [{
name: 'approve',
type: 'function',
stateMutability: 'nonpayable',
inputs: [
{ name: 'spender', type: 'address' },
{ name: 'amount', type: 'uint256' }
],
outputs: [{ name: '', type: 'bool' }]
}],
functionName: 'approve',
args: [marketAddress, parseUnits(amount, 6)] // USDC has 6 decimals
});
// Then supply to Aave
await supply({
market: evmAddress(marketAddress),
amount: {
erc20: {
currency: evmAddress(usdcAddress),
value: parseUnits(amount, 6).toString()
}
},
supplier: evmAddress(address),
chainId: chainId(11155111), // Sepolia testnet
walletClient: window.waap // WaaP as wallet client
});
} catch (error) {
console.error('Supply failed:', error);
} finally {
setIsSupplying(false);
}
};
return (
<div>
<h3>Supply USDC to Earn Yield</h3>
<input
type="number"
value={amount}
onChange={(e) => setAmount(e.target.value)}
placeholder="Amount in USDC"
disabled={isSupplying || isConfirming}
/>
<button
onClick={handleSupply}
disabled={isSupplying || isConfirming || !amount}
>
{isSupplying ? 'Supplying...' : isConfirming ? 'Confirming...' : 'Supply'}
</button>
{isSuccess && <p>Successfully supplied {amount} USDC!</p>}
</div>
);
}
Borrow Against Collateral
Borrow assets using your supplied collateral:
import { borrow } from '@aave/react';
function BorrowAsset() {
const { address } = useAccount();
const [amount, setAmount] = useState('');
const [isBorrowing, setIsBorrowing] = useState(false);
const marketAddress = '0x6Ae43d3271ff6888e7Fc43Fd7321a503ff738951';
const usdcAddress = '0x1c7D4B196Cb0C7B01d743Fbc70B6A3cE8C4C4C4C4';
const handleBorrow = async () => {
if (!amount || parseFloat(amount) <= 0) return;
setIsBorrowing(true);
try {
await borrow({
market: evmAddress(marketAddress),
amount: {
erc20: {
currency: evmAddress(usdcAddress),
value: parseUnits(amount, 6).toString()
}
},
borrower: evmAddress(address),
chainId: chainId(1),
walletClient: window.waap
});
} catch (error) {
console.error('Borrow failed:', error);
} finally {
setIsBorrowing(false);
}
};
return (
<div>
<h3>Borrow USDC</h3>
<input
type="number"
value={amount}
onChange={(e) => setAmount(e.target.value)}
placeholder="Amount to borrow"
disabled={isBorrowing}
/>
<button
onClick={handleBorrow}
disabled={isBorrowing || !amount}
>
{isBorrowing ? 'Borrowing...' : 'Borrow'}
</button>
</div>
);
}
Check Account Health
Monitor your account health and available borrowing power:
import { useAccountHealth, useUserSupplies, useUserBorrows } from '@aave/react';
function AccountHealth() {
const { address } = useAccount();
const { data: health, isLoading: healthLoading } = useAccountHealth({
account: address,
chainId: 1
});
const { data: supplies, isLoading: suppliesLoading } = useUserSupplies({
account: address,
chainId: 1
});
const { data: borrows, isLoading: borrowsLoading } = useUserBorrows({
account: address,
chainId: 1
});
if (healthLoading || suppliesLoading || borrowsLoading) {
return <div>Loading account data...</div>;
}
return (
<div>
<h3>Account Overview</h3>
<div>
<p>Health Factor: {health?.healthFactor ? health.healthFactor.toFixed(2) : 'N/A'}</p>
<p>Total Collateral: ${health?.totalCollateralUSD ? health.totalCollateralUSD.toFixed(2) : '0'}</p>
<p>Total Debt: ${health?.totalDebtUSD ? health.totalDebtUSD.toFixed(2) : '0'}</p>
<p>Available to Borrow: ${health?.availableBorrowsUSD ? health.availableBorrowsUSD.toFixed(2) : '0'}</p>
</div>
<div>
<h4>Your Supplies</h4>
{supplies?.map((supply) => (
<div key={supply.currency.address}>
<p>{supply.currency.symbol}: {supply.supplyBalance} (APY: {(supply.supplyAPY * 100).toFixed(2)}%)</p>
</div>
))}
</div>
<div>
<h4>Your Borrows</h4>
{borrows?.map((borrow) => (
<div key={borrow.currency.address}>
<p>{borrow.currency.symbol}: {borrow.borrowBalance} (APY: {(borrow.borrowAPY * 100).toFixed(2)}%)</p>
</div>
))}
</div>
</div>
);
}
Withdraw and Repay
Complete the lending cycle by withdrawing supplied assets and repaying borrowed amounts:
import { withdraw, repay } from '@aave/react';
function ManagePositions() {
const { address } = useAccount();
const [withdrawAmount, setWithdrawAmount] = useState('');
const [repayAmount, setRepayAmount] = useState('');
const handleWithdraw = async (currencyAddress, amount) => {
try {
await withdraw({
market: evmAddress(marketAddress),
amount: {
erc20: {
currency: evmAddress(currencyAddress),
value: parseUnits(amount, 6).toString()
}
},
supplier: evmAddress(address),
chainId: chainId(1),
walletClient: window.waap
});
} catch (error) {
console.error('Withdraw failed:', error);
}
};
const handleRepay = async (currencyAddress, amount) => {
try {
await repay({
market: evmAddress(marketAddress),
amount: {
erc20: {
currency: evmAddress(currencyAddress),
value: parseUnits(amount, 6).toString()
}
},
borrower: evmAddress(address),
chainId: chainId(1),
walletClient: window.waap
});
} catch (error) {
console.error('Repay failed:', error);
}
};
return (
<div>
<h3>Manage Positions</h3>
{/* Withdraw and repay forms */}
</div>
);
}
Market Data and APY
Display current market rates and available assets:
import { useMarkets } from '@aave/react';
function MarketOverview() {
const { data: markets, isLoading } = useMarkets({
chainId: 1
});
if (isLoading) return <div>Loading markets...</div>;
return (
<div>
<h3>Available Markets</h3>
{markets?.map((market) => (
<div key={market.address} className="market-card">
<h4>{market.currency.symbol}</h4>
<p>Supply APY: {(market.supplyAPY * 100).toFixed(2)}%</p>
<p>Borrow APY: {(market.borrowAPY * 100).toFixed(2)}%</p>
<p>Total Supply: {market.totalSupply}</p>
<p>Total Borrow: {market.totalBorrow}</p>
</div>
))}
</div>
);
}
Security Considerations
When earning yield with WaaP and Aave:
- Monitor Health Factor - Keep your health factor above 1.0 to avoid liquidation
- Diversify Collateral - Don't over-concentrate in a single asset
- Understand Risks - DeFi protocols carry smart contract and market risks
- Start Small - Begin with small amounts to understand the mechanics
- Regular Monitoring - Check your positions regularly, especially during volatile markets
Cross-Chain Yield Opportunities
WaaP supports multiple chains, enabling access to different Aave markets:
import { sepolia, baseSepolia } from 'wagmi/chains';
const aaveMarkets = {
[sepolia.id]: '0x6Ae43d3271ff6888e7Fc43Fd7321a503ff738951', // Sepolia
[baseSepolia.id]: '0x6Ae43d3271ff6888e7Fc43Fd7321a503ff738951' // Base Sepolia
};
Conclusion
WaaP's integration with Aave V3 provides a secure and user-friendly way to earn yield on stablecoins and other assets. The combination of 2PC and 2PC-MPC security and DeFi protocols enables users to participate in lending markets without the complexity of managing private keys directly. This makes yield farming accessible to both beginners and experienced DeFi users.
For more advanced features and integrations, refer to our Methods guide and Customization guide.