Skip to content

Commit c5e3604

Browse files
committed
feat: 🎸 add aave v2 tests, fixtures and constants
1 parent 3252750 commit c5e3604

File tree

5 files changed

+312
-0
lines changed

5 files changed

+312
-0
lines changed

test/aave_v2/01_deposits.test.ts

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import { formatEther, parseEther } from "@ethersproject/units";
2+
import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers";
3+
import { waffle } from "hardhat";
4+
import { ERC20, IAToken, ILendingPool } from "../../typechain";
5+
import { mineBlocks, useChai } from "../utils";
6+
import { aaveV2Fixture } from "./utils/fixtures";
7+
8+
const expect = useChai();
9+
10+
describe("Deposit & Redeem", function () {
11+
let wallet: SignerWithAddress;
12+
let aDAI: IAToken;
13+
let DAI: ERC20;
14+
let pool: ILendingPool;
15+
16+
before(async function () {
17+
({ wallet, aDAI, DAI, lendingPool: pool } = await waffle.loadFixture(aaveV2Fixture));
18+
});
19+
20+
it("deposit", async () => {
21+
// let's check our aDAI balance first
22+
let bal = await aDAI.balanceOf(wallet.address);
23+
console.log("aDAI balance before deposit = ", formatEther(bal));
24+
expect(bal).to.eq(0);
25+
26+
// let's deposit some dai and in return get some adai back!
27+
const amt = parseEther("1000");
28+
// before depositing our dai, we need to approve that the contract
29+
// can withdraw 1000 dai from our account
30+
await DAI.approve(pool.address, amt);
31+
32+
// now let's request the pool to deposit dai in the reserve and give us aDAI
33+
// 1. the address of DAI (which is the underlying token)
34+
// 2. the amount of DAI we want to deposit in the reserve
35+
// 3. the address of the account we want to deposit in
36+
// 4. referral code, we can put 0 in there :)
37+
await pool.deposit(DAI.address, amt, wallet.address, 0);
38+
39+
// now we should get 100 aDAI in return for depositing
40+
bal = await aDAI.balanceOf(wallet.address);
41+
console.log("aDAI balance after deposit = ", formatEther(bal));
42+
expect(bal).to.eq(amt);
43+
});
44+
45+
it("withdraw", async () => {
46+
// let's return the aDAI and get back our DAI
47+
// first let's check the balance of aDAI
48+
const daiBalBefore = await DAI.balanceOf(wallet.address);
49+
console.log("DAI balance before withdraw = ", formatEther(daiBalBefore));
50+
51+
const aDaiBalBefore = await aDAI.balanceOf(wallet.address);
52+
console.log("aDAI balance before withdraw = ", formatEther(aDaiBalBefore));
53+
expect(aDaiBalBefore).to.not.eq(0);
54+
55+
// now, let's withdraw using the pool
56+
// 1. the asset address (DAI)
57+
// 2. amount of DAI to withdraw
58+
// 3. account to withdraw into (our wallet)
59+
await pool.withdraw(DAI.address, aDaiBalBefore, wallet.address);
60+
61+
const daiBalAfter = await DAI.balanceOf(wallet.address);
62+
expect(daiBalAfter).to.eq(daiBalBefore.add(aDaiBalBefore));
63+
console.log("DAI balance after withdraw = ", formatEther(daiBalAfter));
64+
65+
const aDaiBalAfter = await aDAI.balanceOf(wallet.address);
66+
console.log("aDAI balance after withdraw = ", formatEther(aDaiBalAfter));
67+
68+
// NOTE: due to a roundoff issue you might not get 100% of the aTokens
69+
// there might be `0.00xxxx` amount of tokens still left
70+
// so we expect a value in the aDAI contract to be less than or equal to 0.0001
71+
expect(aDaiBalAfter).to.lte(parseEther("0.0001"));
72+
});
73+
74+
it("earn interest", async () => {
75+
// let's get some aDAI first
76+
const amt = parseEther("10000");
77+
await DAI.approve(pool.address, amt);
78+
await pool.deposit(DAI.address, amt, wallet.address, 0);
79+
80+
const balBeforeInterest = await aDAI.balanceOf(wallet.address);
81+
console.log("aDAI balance after deposit = ", formatEther(balBeforeInterest));
82+
83+
// assuming 15 seconds = 1 block
84+
// let's mine 1week of blocks to gain a good amount of interest
85+
const seconds_in_week = 24 * 7 * 60 * 60;
86+
mineBlocks(seconds_in_week / 15);
87+
88+
const newBal = await aDAI.balanceOf(wallet.address);
89+
console.log("aDAI balance after 1 week = ", formatEther(newBal));
90+
expect(newBal.sub(balBeforeInterest)).to.gte(parseEther("0.0000001"));
91+
await pool.withdraw(DAI.address, newBal, wallet.address);
92+
});
93+
});

test/aave_v2/02_borrow.test.ts

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import { formatEther, formatUnits, parseEther, parseUnits } from "@ethersproject/units";
2+
import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers";
3+
import { waffle } from "hardhat";
4+
import { ERC20, ILendingPool } from "../../typechain";
5+
import { useChai } from "../utils";
6+
import { aaveV2Fixture } from "./utils/fixtures";
7+
8+
const expect = useChai();
9+
10+
describe("Borrow and Repay", function () {
11+
let wallet: SignerWithAddress;
12+
let DAI: ERC20;
13+
let USDC: ERC20;
14+
let pool: ILendingPool;
15+
16+
before(async function () {
17+
({ wallet, DAI, USDC, lendingPool: pool } = await waffle.loadFixture(aaveV2Fixture));
18+
19+
// let's add some collateral to our account
20+
// otherwise we won't be able to take a loan
21+
const daiBal = parseEther("1000");
22+
await DAI.approve(pool.address, daiBal);
23+
await pool.deposit(DAI.address, daiBal, wallet.address, 0);
24+
});
25+
26+
it("setting a particular asset as collateral", async () => {
27+
// let's say you have multiple assets deposited into the aave v2 protocol
28+
// but you only want to use one of them, to get the loan
29+
// ie -> in case of liquidations (loan fail), you don't want to lose your other tokens
30+
// in such a case we set an asset reserve to be used as collateral for our account
31+
32+
// we pass in the asset address, and whether it should be used as collateral
33+
// in our case since we deposited DAI, i will use it as collateral
34+
await pool.setUserUseReserveAsCollateral(DAI.address, true);
35+
});
36+
37+
it("borrow stable loan", async () => {
38+
// let's check our USDC balance before we borrow
39+
const balBefore = await USDC.balanceOf(wallet.address);
40+
console.log("balance of USDC before we borrow: ", formatUnits(balBefore, 6));
41+
expect(balBefore).to.eq(0);
42+
43+
// so aave has two kinds of loans
44+
// 1. stable loans - stable apr in the short term, long term adjusts to market change
45+
// 2. variable loans - variable apr, changes based on the supply / demand
46+
// more on this here: https://docs.aave.com/faq/borrowing#what-is-the-difference-between-stable-and-variable-rate
47+
48+
const amt = parseUnits("700", 6); // we can only borrow a fraction of our collateral
49+
await pool.borrow(
50+
USDC.address, // address of asset we want to borrow
51+
amt, // amt we want to borrow
52+
1, // interest rate mode -> 1 = stable, 2 = variable
53+
0, // referral code
54+
wallet.address, // on behalf of which account
55+
);
56+
57+
// let's check our USDC balance after we borrow
58+
const balAfter = await USDC.balanceOf(wallet.address);
59+
console.log("balance of USDC before we borrow: ", formatUnits(balAfter, 6));
60+
expect(balAfter).to.eq(amt);
61+
});
62+
63+
it("change borrow rate mode from stable to variable", async () => {
64+
// let's assume we have a loan in stable borrowing rate, like we do in previous test
65+
// how do we convert it from stable to variable borrowing rate?
66+
// Well, super simple -> we provide the asset address & current rate mode
67+
// since our current rate mode is 1 (stable), we pass in that and aave will replace
68+
// it with variable
69+
await pool.swapBorrowRateMode(USDC.address, 1);
70+
});
71+
72+
it("repay the borrowed sum (ie: replay the loan)", async () => {
73+
const balBefore = await USDC.balanceOf(wallet.address);
74+
console.log("USDC balance before repaying debt = ", formatUnits(balBefore, 6));
75+
expect(balBefore).to.not.eq(0);
76+
77+
// let's allow the pool to withdraw the amount from our USDC account
78+
await USDC.approve(pool.address, balBefore);
79+
// call pool.repay with similar arguments in borrow
80+
// asset, balance we want to repay, borrow rate (variable), and on behalf of address
81+
await pool.repay(USDC.address, balBefore, 2, wallet.address);
82+
const balAfter = await USDC.balanceOf(wallet.address);
83+
84+
console.log("USDC balance after repaying debt = ", formatUnits(balAfter, 6));
85+
expect(balAfter).to.eq(0);
86+
87+
// let's check our total debt now
88+
const data = await pool.getUserAccountData(wallet.address);
89+
console.log("total debt on our account (value in ETH) = ", formatEther(data.totalDebtETH));
90+
expect(data.totalDebtETH).to.lte(parseEther("0.0000001"));
91+
});
92+
});

test/aave_v2/03_stats.test.ts

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import { formatEther, formatUnits, parseEther, parseUnits } from "@ethersproject/units";
2+
import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers";
3+
import { BigNumberish } from "ethers";
4+
import { waffle } from "hardhat";
5+
import { ERC20, ILendingPool, IPriceOracle } from "../../typechain";
6+
import { aaveV2Fixture } from "./utils/fixtures";
7+
8+
describe("Get Statistics", function () {
9+
let wallet: SignerWithAddress;
10+
let DAI: ERC20;
11+
let USDC: ERC20;
12+
let pool: ILendingPool;
13+
let priceOracle: IPriceOracle;
14+
15+
before(async function () {
16+
({ wallet, DAI, USDC, lendingPool: pool, priceOracle } = await waffle.loadFixture(aaveV2Fixture));
17+
18+
// let's add some collateral to our account
19+
// otherwise we won't be able to take a loan
20+
const daiBal = parseEther("1000");
21+
await DAI.approve(pool.address, daiBal);
22+
await pool.deposit(DAI.address, daiBal, wallet.address, 0);
23+
// borrow some amount in USDC too
24+
const usdcBal = parseUnits("500", 6);
25+
await pool.borrow(USDC.address, usdcBal, 1, 0, wallet.address);
26+
});
27+
28+
it("Fetches User Account Data", async () => {
29+
const data = await pool.getUserAccountData(wallet.address);
30+
console.log("The following values are in ETH:");
31+
console.log(`total collateral = ${formatEther(data.totalCollateralETH)} ETH`);
32+
console.log(`total loan/debt = ${formatEther(data.totalDebtETH)} ETH`);
33+
console.log(`amount bororwable = ${formatEther(data.availableBorrowsETH)} ETH`);
34+
console.log(`loan to value = ${formatUnits(data.ltv, 2)}`);
35+
console.log(`user health = ${formatEther(data.healthFactor)}`);
36+
});
37+
38+
it("Get Borrow APR and Supply APY", async () => {
39+
const data = await pool.getReserveData(DAI.address);
40+
// all these rates come in Ray (1e27)
41+
// more info on Ray -> https://medium.com/dapphub/introducing-ds-math-an-innovative-safe-math-library-d58bc88313da
42+
43+
const formatPct = (value: BigNumberish) => {
44+
return formatUnits(value, 27 - 2); // -2 because we want in percentage not decimals
45+
};
46+
console.log("DAI Supply APY = ", formatPct(data.currentLiquidityRate), "%");
47+
console.log("DAI Borrow APR (Stable) = ", formatPct(data.currentStableBorrowRate), "%");
48+
console.log("DAI Borrow APR (Variable) = ", formatPct(data.currentVariableBorrowRate), "%");
49+
});
50+
51+
it("Get the price of token", async () => {
52+
const priceOfDAI = await priceOracle.getAssetPrice(DAI.address);
53+
console.log(`1 DAI = ${formatEther(priceOfDAI)} ETH`);
54+
const priceOfUSDC = await priceOracle.getAssetPrice(USDC.address);
55+
console.log(`1 USDC = ${formatEther(priceOfUSDC)} ETH`);
56+
});
57+
});

test/aave_v2/utils/consts.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export const LendingPoolAddressProviderAddr = "0xB53C1a33016B2DC2fF3653530bfF1848a515c8c5";
2+
export const LendingPoolAddr = "0x7d2768dE32b0b80b7a3454c06BdAc94A69DDc7A9";
3+
export const PriceOracleAddr = "0xA50ba011c48153De246E5192C8f9258A2ba79Ca9";
4+
export const aDAIAddr = "0x028171bCA77440897B824Ca71D1c56caC55b68A3"; // 18 decimals
5+
export const aUSDCAddr = "0xBcca60bB61934080951369a648Fb03DF4F96263C"; // 6 decimals

test/aave_v2/utils/fixtures.ts

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers";
2+
import { ethers } from "hardhat";
3+
import { aDAIAddr, aUSDCAddr, LendingPoolAddr, LendingPoolAddressProviderAddr, PriceOracleAddr } from "./consts";
4+
import {
5+
ERC20,
6+
ERC20__factory,
7+
IAToken,
8+
IAToken__factory,
9+
ILendingPool,
10+
ILendingPoolAddressesProvider,
11+
ILendingPoolAddressesProvider__factory,
12+
ILendingPool__factory,
13+
IPriceOracle,
14+
IPriceOracle__factory,
15+
} from "../../../typechain";
16+
import { DAIAddr, USDCAddr, DAIWhaleAddr } from "../../consts";
17+
import { impersonate } from "../../utils";
18+
import { parseEther } from "@ethersproject/units";
19+
20+
interface AaveV2FixtureResult {
21+
wallet: SignerWithAddress;
22+
addrProvider: ILendingPoolAddressesProvider;
23+
lendingPool: ILendingPool;
24+
priceOracle: IPriceOracle;
25+
aDAI: IAToken;
26+
aUSDC: IAToken;
27+
DAI: ERC20;
28+
USDC: ERC20;
29+
}
30+
31+
export const aaveV2Fixture = async (): Promise<AaveV2FixtureResult> => {
32+
const [wallet] = await ethers.getSigners();
33+
const addrProvider = ILendingPoolAddressesProvider__factory.connect(LendingPoolAddressProviderAddr, wallet);
34+
const lendingPool = ILendingPool__factory.connect(LendingPoolAddr, wallet);
35+
const priceOracle = IPriceOracle__factory.connect(PriceOracleAddr, wallet);
36+
const aDAI = IAToken__factory.connect(aDAIAddr, wallet);
37+
const aUSDC = IAToken__factory.connect(aUSDCAddr, wallet);
38+
const DAI = ERC20__factory.connect(DAIAddr, wallet);
39+
const USDC = ERC20__factory.connect(USDCAddr, wallet);
40+
41+
const [daiWhaleSigner] = await impersonate([DAIWhaleAddr]);
42+
43+
// let's impersonate the account that has a lot of DAI and transfer some to ours
44+
// before we do that, let's send some eth to cover up if the account doesn't
45+
// have any eth
46+
47+
await wallet.sendTransaction({
48+
to: daiWhaleSigner.address,
49+
value: parseEther("1"),
50+
});
51+
52+
// send 10K+ DAI to our master wallet
53+
await DAI.connect(daiWhaleSigner).transfer(wallet.address, parseEther("10000"));
54+
55+
return {
56+
wallet,
57+
addrProvider,
58+
lendingPool,
59+
priceOracle,
60+
aDAI,
61+
aUSDC,
62+
DAI,
63+
USDC,
64+
};
65+
};

0 commit comments

Comments
 (0)