@alchemy/wallet-apis (v5.x.x) is currently in beta but is the recommended replacement for @account-kit/wallet-client (v4.x.x). If you run into any issues, please reach out.Learn how to build DeFi applications that interact with Aave using Alchemy Smart Wallets. This recipe covers supplying and withdrawing assets with both our Core library and our Wallet APIs for seamless user experiences.
- Have DAI available on your chosen chain (default: Arbitrum).
- Alchemy API key, Account config and Gas Manager policy ID for sponsored transactions (you can follow the setup here)
Check out this video walkthrough, and follow along with the quick start below:
-
Clone and setup:
git clone https://github.com/alchemyplatform/smart-wallets-aave.git cd smart-wallets-aave npm install -
Configure environment:
cp .env.example .env # Fill in your ALCHEMY_API_KEY, PRIVATE_KEY, and PAYMASTER_POLICY_ID -
Initialize smart wallet and get address:
npm run account -
Fund your smart wallet: Send 0.01+ DAI to the smart wallet address from step 3
-
Interact with Aave:
npm run supply # Supply assets to Aave npm run withdraw # Withdraw assets from Aave
The configuration is centralized in src/config.ts for easy customization:
import { arbitrum, sepolia } from "@account-kit/infra";
export const config = {
// Network configuration - change this to switch chains
chain: arbitrum, // Can be changed to sepolia, polygon, etc.
// Token and protocol addresses
addresses: {
dai: {
[arbitrum.id]: "0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1",
[sepolia.id]: "0x68194a729C2450ad26072b3D33ADaCbcef39D574",
},
aaveV3Pool: {
[arbitrum.id]: "0x794a61358D6845594F94dc1DB02A252b5b4814aD",
[sepolia.id]: "0x6Ae43d3271ff6888e7Fc43Fd7321a503ff738951",
},
},
// Transaction amounts
amounts: {
deposit: "0.01", // Amount to deposit
withdraw: "0.01", // Amount to withdraw
},
};This approach uses the direct @aa-sdk/core implementation for simpler, more streamlined Aave interactions.
import { LocalAccountSigner } from "@aa-sdk/core";
import { createModularAccountAlchemyClient } from "@account-kit/smart-contracts";
import { alchemy } from "@account-kit/infra";
import { reserve, supply, withdraw } from "@aave/client/actions";
import {
currentChain,
getCurrentTokenAddress,
getCurrentAavePoolAddress,
config,
} from "./config";
// Supply assets to Aave
async function executeSupply() {
const alchemyApiKey = process.env.ALCHEMY_API_KEY!;
const privateKey = process.env.PRIVATE_KEY! as `0x${string}`;
const policyId = process.env.PAYMASTER_POLICY_ID! as `0x${string}`;
// Setup signer
const signer = LocalAccountSigner.privateKeyToAccountSigner(privateKey);
// Create smart account client with gas sponsorship
const smartAccountClient = await createModularAccountAlchemyClient({
transport: alchemy({ apiKey: alchemyApiKey }),
policyId, // Enables gas sponsorship
chain: currentChain,
signer,
});
const accountAddress = smartAccountClient.account.address;
// Get reserve data from Aave
const reserveDataResult = await reserve(client, {
market: evmAddress(getCurrentAavePoolAddress()),
underlyingToken: evmAddress(getCurrentTokenAddress()),
chainId: chainId(currentChain.id),
user: accountAddress,
});
const reserveData = reserveDataResult.value;
// Get supply transaction data
const supplyResult = await supply(client, {
market: reserveData.market.address,
amount: {
erc20: {
currency: reserveData.underlyingToken.address,
value: config.amounts.deposit,
},
},
sender: evmAddress(accountAddress),
chainId: reserveData.market.chain.chainId,
});
let supplyData = supplyResult.value;
// Handle transactions (approval + supply or just supply)
if (supplyData.__typename === "ApprovalRequired") {
// Send approval transaction
const approvalTx = await smartAccountClient.sendUserOperation({
account: smartAccountClient.account,
uo: {
target: supplyData.approval.to,
data: supplyData.approval.data,
},
});
await smartAccountClient.getUserOperationReceipt(approvalTx.hash);
// Send supply transaction
const supplyTx = await smartAccountClient.sendUserOperation({
account: smartAccountClient.account,
uo: {
target: supplyData.originalTransaction.to,
data: supplyData.originalTransaction.data,
value: BigInt(supplyData.originalTransaction.value),
},
});
console.log("Supply completed! Hash:", supplyTx.hash);
}
}
// Withdraw assets from Aave
async function executeWithdraw() {
// Setup same as supply...
const smartAccountClient = await createModularAccountAlchemyClient({
transport: alchemy({ apiKey: alchemyApiKey }),
policyId,
chain: currentChain,
signer,
});
// Get withdraw transaction data
const withdrawResult = await withdraw(client, {
market: reserveData.market.address,
amount: {
erc20: {
currency: reserveData.underlyingToken.address,
value: { exact: config.amounts.withdraw }, // or { max: true } for all
},
},
sender: evmAddress(accountAddress),
chainId: reserveData.market.chain.chainId,
});
let withdrawData = withdrawResult.value;
if (withdrawData.__typename === "TransactionRequest") {
const withdrawTx = await smartAccountClient.sendUserOperation({
account: smartAccountClient.account,
uo: {
target: withdrawData.to,
data: withdrawData.data,
value: BigInt(withdrawData.value),
},
});
console.log("Withdrawal completed! Hash:", withdrawTx.hash);
}
}The repository includes convenient scripts for different approaches:
# Core implementation (recommended)
npm run core:account # Initialize account
npm run core:supply # Supply to Aave
npm run core:withdraw # Withdraw from Aave
# Wallet APIs implementation
npm run wallet:account # Initialize account
npm run wallet:supply # Supply to Aave
npm run wallet:withdraw # Withdraw from Aave
# Default scripts (use Core)
npm run account # Defaults to core:account
npm run supply # Defaults to core:supply
npm run withdraw # Defaults to core:withdrawYou can now interact with Aave protocol through smart wallets with sponsored gas fees, enabling seamless DeFi experiences for supplying, withdrawing, and managing liquidity without gas complexity.