китаев.tech

Deposit в Aave V3

Как внести WETH в Aave V3 через Pool.supply, получить aWETH и проверить позицию в ethers v6.

Deposit в Aave V3 называется supply

В интерфейсе Aave пользователь делает deposit. В контракте Aave V3 это метод supply.

Flow похож на другие DeFi-протоколы: сначала пользователь разрешает Pool списать ERC-20 токены через approve, затем вызывает supply и получает aToken. Для WETH это aWETH.


Вносим WETH в Aave V3

Пример запускается на локальном fork Ethereum mainnet. Он использует настоящие mainnet-контракты Aave V3 и WETH, но не отправляет транзакции в реальную сеть.

Создайте проект:

bash
mkdir aave-v3-deposit-example
cd aave-v3-deposit-example

npm init -y
npm pkg set type="module"
npm install ethers tsx
npm install --save-dev hardhat typescript @types/node
mkdir src

Создайте файл hardhat.config.js:

javascript
export default {
  solidity: "0.8.20",
};

В первом терминале запустите mainnet fork:

bash
npx hardhat node --fork https://ethereum-rpc.publicnode.com

Создайте файл src/deposit-weth.ts:

typescript
import { ethers } from "ethers";

const LOCAL_FORK_RPC_URL = "http://127.0.0.1:8545";

const HARDHAT_ACCOUNT_PRIVATE_KEY =
  "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80";

const AAVE_V3_POOL_ADDRESS = "0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2";
const WETH_ADDRESS = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2";
const AWETH_ADDRESS = "0x4d5F47FA6A74757f35C14fD3a6Ef8E3C9BC514E8";

const WETH_DECIMALS = 18;
const REFERRAL_CODE = 0;

const WETH_ABI = [
  "function deposit() external payable",
  "function approve(address spender,uint256 amount) external returns (bool)",
  "function allowance(address owner,address spender) external view returns (uint256)",
  "function balanceOf(address account) external view returns (uint256)",
];

const ATOKEN_ABI = [
  "function balanceOf(address account) external view returns (uint256)",
];

const AAVE_POOL_ABI = [
  "function supply(address asset,uint256 amount,address onBehalfOf,uint16 referralCode) external",
];

const provider = new ethers.JsonRpcProvider(LOCAL_FORK_RPC_URL);
const wallet = new ethers.Wallet(HARDHAT_ACCOUNT_PRIVATE_KEY, provider);

const weth = new ethers.Contract(WETH_ADDRESS, WETH_ABI, wallet);
const aWeth = new ethers.Contract(AWETH_ADDRESS, ATOKEN_ABI, provider);
const pool = new ethers.Contract(AAVE_V3_POOL_ADDRESS, AAVE_POOL_ABI, wallet);

const amount = ethers.parseUnits("1", WETH_DECIMALS);

console.log(`User: ${wallet.address}`);

const wrapTx = await weth.deposit({ value: amount });
await wrapTx.wait();

const wethBefore = await weth.balanceOf(wallet.address);
const aWethBefore = await aWeth.balanceOf(wallet.address);

console.log(`WETH before: ${ethers.formatUnits(wethBefore, WETH_DECIMALS)}`);
console.log(`aWETH before: ${ethers.formatUnits(aWethBefore, WETH_DECIMALS)}`);

const approveTx = await weth.approve(AAVE_V3_POOL_ADDRESS, amount);
await approveTx.wait();

const allowance = await weth.allowance(wallet.address, AAVE_V3_POOL_ADDRESS);
console.log(`Pool allowance: ${ethers.formatUnits(allowance, WETH_DECIMALS)}`);

const supplyTx = await pool.supply(
  WETH_ADDRESS,
  amount,
  wallet.address,
  REFERRAL_CODE,
);

const receipt = await supplyTx.wait();

const wethAfter = await weth.balanceOf(wallet.address);
const aWethAfter = await aWeth.balanceOf(wallet.address);

console.log(`Supply tx: ${receipt?.hash}`);
console.log(`WETH after: ${ethers.formatUnits(wethAfter, WETH_DECIMALS)}`);
console.log(`aWETH after: ${ethers.formatUnits(aWethAfter, WETH_DECIMALS)}`);

Во втором терминале запустите deposit:

bash
npx tsx src/deposit-weth.ts

Скрипт выведет примерно такой результат:

text
User: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
WETH before: 1.0
aWETH before: 0.0
Pool allowance: 1.0
Supply tx: 0x...
WETH after: 0.0
aWETH after: 1.0

На реальном mainnet aWETH баланс со временем растёт за счёт процентов. На локальном fork вы увидите состояние на момент запуска.

Как это работает

AAVE_V3_POOL_ADDRESS — адрес Aave V3 Pool в Ethereum mainnet.

WETH_ADDRESS — underlying asset, который пользователь вносит в протокол.

AWETH_ADDRESS — aToken для WETH. Aave выпускает его пользователю после supply.

weth.deposit() — превращает ETH локального Hardhat-аккаунта в WETH. Aave Pool принимает ERC-20 токен, а не нативный ETH.

weth.approve(AAVE_V3_POOL_ADDRESS, amount) — разрешает Aave Pool списать WETH с адреса пользователя.

pool.supply(WETH_ADDRESS, amount, wallet.address, 0) — вносит WETH в Aave V3 и минтит aWETH на onBehalfOf.

referralCode: 0 — стандартное значение, если у приложения нет referral-интеграции с Aave.

Что такое aToken

aToken — это токен позиции поставщика ликвидности в Aave.

Когда пользователь вносит WETH, он получает aWETH. Баланс aWETH отражает долю пользователя в пуле и увеличивается вместе с начисленными процентами. При withdraw Aave сжигает aWETH и возвращает underlying asset.

Для разработчика важное следствие: позиция в Aave читается не по балансу WETH, а по балансу соответствующего aToken.

Частые ошибки

Вызвать deposit, а не supply → в Aave V3 контрактный метод называется supply. deposit — это пользовательский термин из интерфейса.

Забыть approve → Pool не сможет списать WETH и транзакция откатится.

Передать нативный ETH вместо WETHsupply принимает ERC-20 asset. Для ETH сначала используйте WETH или отдельный gateway-flow.

Проверять только WETH баланс после supply → после deposit WETH уходит в Pool, а позиция появляется как aWETH.


Что дальше

Материалы китаev.tech публикуются в образовательных целях и не являются инвестиционной рекомендацией. Примеры кода и описания протоколов — для обучения; использование в продакшне на ваш собственный риск. Дисклеймер и политика конфиденциальности.