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 Human Wallet'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 Human Wallet, allowing users to supply assets to earn interest and borrow against their collateral.
Demo and source code​
This recipe is also available as an example project.
→ View live demo | → Browse source code
What are we cooking?​
A React application based on Human Wallet quick start example with WAGMI that connects Human Wallet 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​
- Human Wallet - 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 Human Wallet quick start example​
npx gitpick holonym-foundation/human-wallet-examples/tree/main/human-wallet-wagmi-nextjs
cd human-wallet-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.silk // Human Wallet 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.silk
});
} 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.silk
});
} 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.silk
});
} 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 Human Wallet 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​
Human Wallet 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​
Human Wallet'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.