├── .husky ├── .gitignore ├── pre-commit └── commit-msg ├── .gitattributes ├── .czrc ├── tasks ├── task-names.ts ├── accounts.ts └── clean.ts ├── .solhintignore ├── .yarnrc.yml ├── .commitlintrc.js ├── extras └── defi-playground-logo.png ├── .env.example ├── .prettierignore ├── contracts ├── erc20.sol ├── aave_v2 │ ├── IPriceOracle.sol │ ├── IScaledBalanceToken.sol │ ├── DataTypes.sol │ ├── ILendingPoolAddressesProvider.sol │ ├── IAToken.sol │ └── ILendingPool.sol └── compound │ ├── comptroller.sol │ └── ctoken.sol ├── types ├── index.ts └── augmentations.d.ts ├── .eslintignore ├── test ├── compound │ ├── utils │ │ ├── consts.ts │ │ └── fixtures.ts │ ├── 02_borrow.test.ts │ ├── 03_prices.test.ts │ └── 01_deposits.test.ts ├── consts.ts ├── aave_v2 │ ├── utils │ │ ├── consts.ts │ │ └── fixtures.ts │ ├── 03_stats.test.ts │ ├── 01_deposits.test.ts │ └── 02_borrow.test.ts └── utils.ts ├── .editorconfig ├── .prettierrc ├── .gitignore ├── .solhint.json ├── .eslintrc.yaml ├── tsconfig.json ├── .solcover.js ├── LICENSE ├── hardhat.config.ts ├── package.json └── README.md /.husky/.gitignore: -------------------------------------------------------------------------------- 1 | _ 2 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.sol linguist-language=Solidity -------------------------------------------------------------------------------- /.czrc: -------------------------------------------------------------------------------- 1 | { 2 | "path": "cz-conventional-changelog" 3 | } 4 | -------------------------------------------------------------------------------- /tasks/task-names.ts: -------------------------------------------------------------------------------- 1 | export const TASK_ACCOUNTS: string = "accounts"; 2 | -------------------------------------------------------------------------------- /.solhintignore: -------------------------------------------------------------------------------- 1 | # folders 2 | .yarn/ 3 | build/ 4 | dist/ 5 | node_modules/ 6 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | npx lint-staged -------------------------------------------------------------------------------- /.yarnrc.yml: -------------------------------------------------------------------------------- 1 | nodeLinker: "node-modules" 2 | yarnPath: ".yarn/releases/yarn-berry.cjs" 3 | -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | npx commitlint -e 5 | -------------------------------------------------------------------------------- /.commitlintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ["@commitlint/config-conventional"], 3 | }; 4 | -------------------------------------------------------------------------------- /extras/defi-playground-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xDaksh/defi-playground/HEAD/extras/defi-playground-logo.png -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | ALCHEMY_API_KEY=zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz 2 | MNEMONIC=here is where your twelve words mnemonic should be put my friend 3 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # folders 2 | artifacts/ 3 | build/ 4 | cache/ 5 | coverage/ 6 | dist/ 7 | lib/ 8 | node_modules/ 9 | typechain/ 10 | 11 | # files 12 | coverage.json 13 | -------------------------------------------------------------------------------- /contracts/erc20.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 5 | 6 | interface ERC20 is IERC20 {} 7 | -------------------------------------------------------------------------------- /types/index.ts: -------------------------------------------------------------------------------- 1 | import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/dist/src/signer-with-address"; 2 | 3 | export interface Signers { 4 | admin: SignerWithAddress; 5 | } 6 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | # folders 2 | artifacts/ 3 | build/ 4 | cache/ 5 | coverage/ 6 | dist/ 7 | lib/ 8 | node_modules/ 9 | typechain/ 10 | 11 | # files 12 | .solcover.js 13 | coverage.json 14 | -------------------------------------------------------------------------------- /contracts/aave_v2/IPriceOracle.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.0; 2 | 3 | interface IPriceOracle { 4 | /*********** 5 | @dev returns the asset price in ETH 6 | */ 7 | function getAssetPrice(address asset) external view returns (uint256); 8 | } 9 | -------------------------------------------------------------------------------- /test/compound/utils/consts.ts: -------------------------------------------------------------------------------- 1 | export const cUSDCAddr = "0x39aa39c021dfbae8fac545936693ac917d5e7563"; 2 | export const cDAIAddr = "0x5d3a536e4d6dbd6114cc1ead35777bab948e3643"; 3 | export const comptrollerAddr = "0x3d9819210A31b4961b30EF54bE2aeD79B9c9Cd3B"; 4 | -------------------------------------------------------------------------------- /contracts/compound/comptroller.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | interface ComptrollerInterface { 5 | function enterMarkets(address[] calldata cTokens) external returns (uint256[] memory); 6 | 7 | function exitMarket(address cToken) external returns (uint256); 8 | } 9 | -------------------------------------------------------------------------------- /test/consts.ts: -------------------------------------------------------------------------------- 1 | export const USDCAddr = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"; 2 | export const DAIAddr = "0x6b175474e89094c44da98b954eedeac495271d0f"; 3 | export const USDCWhaleAddr = "0x47ac0fb4f2d84898e4d9e7b4dab3c24507a6d503"; 4 | export const DAIWhaleAddr = "0x47ac0fb4f2d84898e4d9e7b4dab3c24507a6d503"; 5 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig http://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | # All files 7 | [*] 8 | charset = utf-8 9 | end_of_line = lf 10 | indent_size = 2 11 | indent_style = space 12 | insert_final_newline = true 13 | trim_trailing_whitespace = true 14 | 15 | [*.sol] 16 | indent_size = 4 17 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "arrowParens": "avoid", 3 | "bracketSpacing": true, 4 | "endOfLine":"auto", 5 | "printWidth": 120, 6 | "singleQuote": false, 7 | "tabWidth": 2, 8 | "trailingComma": "all", 9 | "overrides": [ 10 | { 11 | "files": "*.sol", 12 | "options": { 13 | "tabWidth": 4 14 | } 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # folders 2 | .coverage_artifacts/ 3 | .coverage_cache/ 4 | .coverage_contracts/ 5 | .yarn/* 6 | !.yarn/releases 7 | !.yarn/plugins 8 | artifacts/ 9 | build/ 10 | cache/ 11 | coverage/ 12 | dist/ 13 | lib/ 14 | node_modules/ 15 | typechain/ 16 | 17 | # files 18 | *.env 19 | *.log 20 | *.tsbuildinfo 21 | coverage.json 22 | npm-debug.log* 23 | yarn-debug.log* 24 | yarn-error.log* 25 | -------------------------------------------------------------------------------- /types/augmentations.d.ts: -------------------------------------------------------------------------------- 1 | // eslint-disable @typescript-eslint/no-explicit-any 2 | import { Fixture } from "ethereum-waffle"; 3 | 4 | import { Signers } from "./"; 5 | import { Greeter } from "../typechain/Greeter"; 6 | 7 | declare module "mocha" { 8 | export interface Context { 9 | greeter: Greeter; 10 | loadFixture: (fixture: Fixture) => Promise; 11 | signers: Signers; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /tasks/accounts.ts: -------------------------------------------------------------------------------- 1 | import { Signer } from "@ethersproject/abstract-signer"; 2 | import { task } from "hardhat/config"; 3 | 4 | import { TASK_ACCOUNTS } from "./task-names"; 5 | 6 | task(TASK_ACCOUNTS, "Prints the list of accounts", async (_taskArgs, hre) => { 7 | const accounts: Signer[] = await hre.ethers.getSigners(); 8 | 9 | for (const account of accounts) { 10 | console.log(await account.getAddress()); 11 | } 12 | }); 13 | -------------------------------------------------------------------------------- /test/aave_v2/utils/consts.ts: -------------------------------------------------------------------------------- 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 6 | -------------------------------------------------------------------------------- /tasks/clean.ts: -------------------------------------------------------------------------------- 1 | import fsExtra from "fs-extra"; 2 | import { TASK_CLEAN } from "hardhat/builtin-tasks/task-names"; 3 | import { task } from "hardhat/config"; 4 | 5 | task(TASK_CLEAN, "Overrides the standard clean task", async function (_taskArgs, { config }, runSuper) { 6 | await fsExtra.remove("./coverage"); 7 | await fsExtra.remove("./coverage.json"); 8 | if (config.typechain?.outDir) { 9 | await fsExtra.remove(config.typechain.outDir); 10 | } 11 | await runSuper(); 12 | }); 13 | -------------------------------------------------------------------------------- /.solhint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "solhint:recommended", 3 | "plugins": ["prettier"], 4 | "rules": { 5 | "code-complexity": ["error", 7], 6 | "compiler-version": ["error", "^0.8.0"], 7 | "const-name-snakecase": "off", 8 | "constructor-syntax": "error", 9 | "func-visibility": ["error", { "ignoreConstructors": true }], 10 | "max-line-length": ["error", 120], 11 | "not-rely-on-time": "off", 12 | "prettier/prettier": [ 13 | "error", 14 | { 15 | "endOfLine": "auto" 16 | } 17 | ], 18 | "reason-string": ["warn", { "maxLength": 64 }] 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /.eslintrc.yaml: -------------------------------------------------------------------------------- 1 | extends: 2 | - "eslint:recommended" 3 | - "plugin:@typescript-eslint/eslint-recommended" 4 | - "plugin:@typescript-eslint/recommended" 5 | - "prettier" 6 | parser: "@typescript-eslint/parser" 7 | parserOptions: 8 | project: "tsconfig.json" 9 | plugins: 10 | - "@typescript-eslint" 11 | root: true 12 | rules: 13 | "@typescript-eslint/no-floating-promises": 14 | - error 15 | - ignoreIIFE: true 16 | ignoreVoid: true 17 | "@typescript-eslint/no-inferrable-types": "off" 18 | "@typescript-eslint/no-unused-vars": 19 | - error 20 | - argsIgnorePattern: _ 21 | varsIgnorePattern: _ 22 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "esModuleInterop": true, 4 | "forceConsistentCasingInFileNames": true, 5 | "lib": ["es5", "es6"], 6 | "module": "commonjs", 7 | "moduleResolution": "node", 8 | "noImplicitAny": true, 9 | "outDir": "dist", 10 | "resolveJsonModule": true, 11 | "sourceMap": true, 12 | "strict": true, 13 | "target": "es5" 14 | }, 15 | "exclude": ["artifacts", "node_modules"], 16 | "files": ["./hardhat.config.ts"], 17 | "include": [ 18 | "artifacts/**/*", 19 | "artifacts/**/*.json", 20 | "scripts/**/*", 21 | "tasks/**/*", 22 | "test/**/*", 23 | "typechain/**/*", 24 | "types/**/*" 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /.solcover.js: -------------------------------------------------------------------------------- 1 | const shell = require("shelljs"); 2 | 3 | // The environment variables are loaded in hardhat.config.ts 4 | const mnemonic = process.env.MNEMONIC; 5 | if (!mnemonic) { 6 | throw new Error("Please set your MNEMONIC in a .env file"); 7 | } 8 | 9 | module.exports = { 10 | istanbulReporter: ["html", "lcov"], 11 | onCompileComplete: async function (_config) { 12 | await run("typechain"); 13 | }, 14 | onIstanbulComplete: async function (_config) { 15 | // We need to do this because solcover generates bespoke artifacts. 16 | shell.rm("-rf", "./artifacts"); 17 | shell.rm("-rf", "./typechain"); 18 | }, 19 | providerOptions: { 20 | mnemonic, 21 | }, 22 | skipFiles: ["mocks", "test"], 23 | }; 24 | -------------------------------------------------------------------------------- /test/utils.ts: -------------------------------------------------------------------------------- 1 | import { use, expect } from "chai"; 2 | import hre from "hardhat"; 3 | import chaiAsPromised from "chai-as-promised"; 4 | import { solidity } from "ethereum-waffle"; 5 | import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; 6 | 7 | export const useChai = (): typeof expect => { 8 | use(chaiAsPromised); 9 | use(solidity); 10 | 11 | return expect; 12 | }; 13 | 14 | export const impersonate = async (accounts: string[]): Promise => { 15 | const signers: SignerWithAddress[] = []; 16 | for (const account of accounts) { 17 | await hre.network.provider.request({ 18 | method: "hardhat_impersonateAccount", 19 | params: [account], 20 | }); 21 | 22 | signers.push(await hre.ethers.getSigner(account)); 23 | } 24 | 25 | return signers; 26 | }; 27 | 28 | export const mineBlocks = async (blocks: number): Promise => { 29 | for (let index = 0; index < blocks; index++) { 30 | await hre.network.provider.send("evm_mine"); 31 | } 32 | }; 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Daksh Miglani 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /contracts/aave_v2/IScaledBalanceToken.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: agpl-3.0 2 | pragma solidity ^0.8.0; 3 | 4 | interface IScaledBalanceToken { 5 | /** 6 | * @dev Returns the scaled balance of the user. The scaled balance is the sum of all the 7 | * updated stored balance divided by the reserve's liquidity index at the moment of the update 8 | * @param user The user whose balance is calculated 9 | * @return The scaled balance of the user 10 | **/ 11 | function scaledBalanceOf(address user) external view returns (uint256); 12 | 13 | /** 14 | * @dev Returns the scaled balance of the user and the scaled total supply. 15 | * @param user The address of the user 16 | * @return The scaled balance of the user 17 | * @return The scaled balance and the scaled total supply 18 | **/ 19 | function getScaledUserBalanceAndSupply(address user) external view returns (uint256, uint256); 20 | 21 | /** 22 | * @dev Returns the scaled total supply of the variable debt token. Represents sum(debt/index) 23 | * @return The scaled total supply 24 | **/ 25 | function scaledTotalSupply() external view returns (uint256); 26 | } 27 | -------------------------------------------------------------------------------- /contracts/aave_v2/DataTypes.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: agpl-3.0 2 | pragma solidity ^0.8.0; 3 | 4 | library DataTypes { 5 | // refer to the whitepaper, section 1.1 basic concepts for a formal description of these properties. 6 | struct ReserveData { 7 | //stores the reserve configuration 8 | ReserveConfigurationMap configuration; 9 | //the liquidity index. Expressed in ray 10 | uint128 liquidityIndex; 11 | //variable borrow index. Expressed in ray 12 | uint128 variableBorrowIndex; 13 | //the current supply rate. Expressed in ray 14 | uint128 currentLiquidityRate; 15 | //the current variable borrow rate. Expressed in ray 16 | uint128 currentVariableBorrowRate; 17 | //the current stable borrow rate. Expressed in ray 18 | uint128 currentStableBorrowRate; 19 | uint40 lastUpdateTimestamp; 20 | //tokens addresses 21 | address aTokenAddress; 22 | address stableDebtTokenAddress; 23 | address variableDebtTokenAddress; 24 | //address of the interest rate strategy 25 | address interestRateStrategyAddress; 26 | //the id of the reserve. Represents the position in the list of the active reserves 27 | uint8 id; 28 | } 29 | 30 | struct ReserveConfigurationMap { 31 | //bit 0-15: LTV 32 | //bit 16-31: Liq. threshold 33 | //bit 32-47: Liq. bonus 34 | //bit 48-55: Decimals 35 | //bit 56: Reserve is active 36 | //bit 57: reserve is frozen 37 | //bit 58: borrowing is enabled 38 | //bit 59: stable rate borrowing enabled 39 | //bit 60-63: reserved 40 | //bit 64-79: reserve factor 41 | uint256 data; 42 | } 43 | 44 | struct UserConfigurationMap { 45 | uint256 data; 46 | } 47 | 48 | enum InterestRateMode { NONE, STABLE, VARIABLE } 49 | } 50 | -------------------------------------------------------------------------------- /test/aave_v2/utils/fixtures.ts: -------------------------------------------------------------------------------- 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 => { 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 | }; 66 | -------------------------------------------------------------------------------- /test/compound/02_borrow.test.ts: -------------------------------------------------------------------------------- 1 | import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; 2 | import { ComptrollerInterface, CTokenInterface, ERC20 } from "../../typechain"; 3 | import { useChai } from "../utils"; 4 | import { compoundFixture } from "./utils/fixtures"; 5 | import { waffle } from "hardhat"; 6 | const expect = useChai(); 7 | 8 | describe("Compound: Borrow and Payback", function () { 9 | let wallet: SignerWithAddress; 10 | let USDC: ERC20; 11 | let DAI: ERC20; 12 | let cUSDC: CTokenInterface; 13 | let cDAI: CTokenInterface; 14 | let comptroller: ComptrollerInterface; 15 | 16 | before(async function () { 17 | // go read the code for this fixture to understand 18 | // how we get the wallet, erc20 USDC token, ctoken of USDC, etc. 19 | ({ wallet, USDC, DAI, cUSDC, cDAI, comptroller } = await waffle.loadFixture(compoundFixture)); 20 | }); 21 | 22 | it("Compound: deposit USDC as collateral and borrow DAI", async () => { 23 | // approve cUSDC to take out 1000 USDC from wallet 24 | await USDC.approve(cUSDC.address, 1000); 25 | // deposit 1000 USDC tokens and get the cUSDC in return 26 | await cUSDC.mint(1000); 27 | 28 | // set USDC as collateral 29 | const markets = [cUSDC.address]; 30 | await comptroller.enterMarkets(markets); 31 | 32 | // using that collateral, let's borrow some DAI 33 | // but we can only borrow ~80% of what our collateral is 34 | await cDAI.borrow(799); 35 | 36 | // check balanceOf DAI 37 | expect(await DAI.balanceOf(wallet.address)).to.eq(799); 38 | }); 39 | 40 | it("Compound: check the borrow balance of DAI", async () => { 41 | const bal = await cDAI.callStatic.borrowBalanceCurrent(wallet.address); 42 | expect(bal).to.eq(799); 43 | }); 44 | 45 | it("Compound: repay the borrowed DAI", async () => { 46 | // approve cDAI to take out 799 DAI from your account 47 | await DAI.approve(cDAI.address, 799); 48 | // repay the borrowed amount 49 | await cDAI.repayBorrow(799); 50 | // note: there's also a repayBorrowBehalf that allows you to repay someone else's debt 51 | 52 | // cDAI borrowBalanceCurrent should be 0 now 53 | expect(await cDAI.callStatic.borrowBalanceCurrent(wallet.address)).to.eq(0); 54 | // DAI balance should be 0 now 55 | expect(await DAI.balanceOf(wallet.address)).to.eq(0); 56 | }); 57 | }); 58 | -------------------------------------------------------------------------------- /test/compound/utils/fixtures.ts: -------------------------------------------------------------------------------- 1 | import { parseEther } from "@ethersproject/units"; 2 | import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; 3 | import { ethers } from "hardhat"; 4 | import { 5 | ComptrollerInterface, 6 | ComptrollerInterface__factory, 7 | CTokenInterface, 8 | CTokenInterface__factory, 9 | ERC20, 10 | ERC20__factory, 11 | } from "../../../typechain"; 12 | import { DAIAddr, USDCAddr, USDCWhaleAddr } from "../../consts"; 13 | import { impersonate } from "../../utils"; 14 | import { cUSDCAddr, cDAIAddr, comptrollerAddr } from "./consts"; 15 | 16 | interface CompoundFixtureResult { 17 | wallet: SignerWithAddress; 18 | cUSDC: CTokenInterface; 19 | cDAI: CTokenInterface; 20 | USDC: ERC20; 21 | DAI: ERC20; 22 | comptroller: ComptrollerInterface; 23 | } 24 | 25 | export const compoundFixture = async (): Promise => { 26 | // ethers gives us 10 wallets, let's use the second one 27 | // since we already used the first one in aave 28 | // get a wallet with 1000 ETH 29 | const [_, wallet] = await ethers.getSigners(); 30 | 31 | // get the compound controller -> comptroller! 32 | const comptroller = ComptrollerInterface__factory.connect(comptrollerAddr, wallet); 33 | 34 | // get the cerc20 token for USDC 35 | const cUSDC = CTokenInterface__factory.connect(cUSDCAddr, wallet); 36 | // get the cerc20 token for DAI 37 | const cDAI = CTokenInterface__factory.connect(cDAIAddr, wallet); 38 | // get the erc20 token for USDC 39 | const USDC = ERC20__factory.connect(USDCAddr, wallet); 40 | // get the erc20 token for DAI 41 | const DAI = ERC20__factory.connect(DAIAddr, wallet); 42 | 43 | // a whale is an account with a ton of tokens/ether 44 | // we impersonate a whale on a local network to acquire those tokens 45 | // we're sending some ether to cover the transfer fees 46 | await wallet.sendTransaction({ 47 | value: parseEther("1"), 48 | to: USDCWhaleAddr, 49 | }); 50 | 51 | // impersonate the whale 52 | await impersonate([USDCWhaleAddr]); 53 | const USDCWhale = await ethers.getSigner(USDCWhaleAddr); 54 | 55 | // originally we should have 0 USDC in our wallet 56 | // after transfering it from whale to us 57 | // we should have 10,000 USDC 58 | await USDC.connect(USDCWhale).transfer(wallet.address, 10000); 59 | 60 | return { 61 | wallet, 62 | cUSDC, 63 | cDAI, 64 | USDC, 65 | DAI, 66 | comptroller, 67 | }; 68 | }; 69 | -------------------------------------------------------------------------------- /contracts/compound/ctoken.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | interface CTokenInterface { 5 | function mint(uint256 mintAmount) external returns (uint256); 6 | 7 | function borrow(uint256 borrowAmount) external returns (uint256); 8 | 9 | function repayBorrow(uint256 repayAmount) external returns (uint256); 10 | 11 | function redeemUnderlying(uint256 redeemAmount) external returns (uint256); 12 | 13 | function redeem(uint256 redeemTokens) external returns (uint256); 14 | 15 | function exchangeRateCurrent() external returns (uint256); 16 | 17 | function balanceOf(address owner) external view returns (uint256 balance); 18 | 19 | function underlying() external view returns (address); 20 | 21 | function approve(address, uint256) external; 22 | 23 | function transfer(address, uint256) external returns (bool); 24 | 25 | function balanceOfUnderlying(address owner) external returns (uint256); 26 | 27 | function exchangeRateStored() external view returns (uint256); 28 | 29 | function transferFrom( 30 | address, 31 | address, 32 | uint256 33 | ) external returns (bool); 34 | 35 | function getCash() external view returns (uint256); 36 | 37 | function borrowBalanceCurrent(address account) external returns (uint256); 38 | 39 | function totalBorrowsCurrent() external returns (uint256); 40 | 41 | function totalSupply() external returns (uint256); 42 | 43 | function supplyRatePerBlock() external view returns (uint256); 44 | 45 | function borrowRatePerBlock() external view returns (uint256); 46 | 47 | event Mint(address minter, uint256 mintAmount, uint256 mintTokens); 48 | event Redeem(address redeemer, uint256 redeemAmount, uint256 redeemTokens); 49 | event Borrow(address borrower, uint256 borrowAmount, uint256 accountBorrows, uint256 totalBorrows); 50 | event RepayBorrow( 51 | address payer, 52 | address borrower, 53 | uint256 repayAmount, 54 | uint256 accountBorrows, 55 | uint256 totalBorrows 56 | ); 57 | event LiquidateBorrow( 58 | address liquidator, 59 | address borrower, 60 | uint256 repayAmount, 61 | address cTokenCollateral, 62 | uint256 seizeTokens 63 | ); 64 | event Transfer(address indexed from, address indexed to, uint256 amount); 65 | event Approval(address indexed owner, address indexed spender, uint256 amount); 66 | } 67 | -------------------------------------------------------------------------------- /contracts/aave_v2/ILendingPoolAddressesProvider.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: agpl-3.0 2 | pragma solidity ^0.8.0; 3 | 4 | /** 5 | * @title LendingPoolAddressesProvider contract 6 | * @dev Main registry of addresses part of or connected to the protocol, including permissioned roles 7 | * - Acting also as factory of proxies and admin of those, so with right to change its implementations 8 | * - Owned by the Aave Governance 9 | * @author Aave 10 | **/ 11 | interface ILendingPoolAddressesProvider { 12 | event MarketIdSet(string newMarketId); 13 | event LendingPoolUpdated(address indexed newAddress); 14 | event ConfigurationAdminUpdated(address indexed newAddress); 15 | event EmergencyAdminUpdated(address indexed newAddress); 16 | event LendingPoolConfiguratorUpdated(address indexed newAddress); 17 | event LendingPoolCollateralManagerUpdated(address indexed newAddress); 18 | event PriceOracleUpdated(address indexed newAddress); 19 | event LendingRateOracleUpdated(address indexed newAddress); 20 | event ProxyCreated(bytes32 id, address indexed newAddress); 21 | event AddressSet(bytes32 id, address indexed newAddress, bool hasProxy); 22 | 23 | function getMarketId() external view returns (string memory); 24 | 25 | function setMarketId(string calldata marketId) external; 26 | 27 | function setAddress(bytes32 id, address newAddress) external; 28 | 29 | function setAddressAsProxy(bytes32 id, address impl) external; 30 | 31 | function getAddress(bytes32 id) external view returns (address); 32 | 33 | function getLendingPool() external view returns (address); 34 | 35 | function setLendingPoolImpl(address pool) external; 36 | 37 | function getLendingPoolConfigurator() external view returns (address); 38 | 39 | function setLendingPoolConfiguratorImpl(address configurator) external; 40 | 41 | function getLendingPoolCollateralManager() external view returns (address); 42 | 43 | function setLendingPoolCollateralManager(address manager) external; 44 | 45 | function getPoolAdmin() external view returns (address); 46 | 47 | function setPoolAdmin(address admin) external; 48 | 49 | function getEmergencyAdmin() external view returns (address); 50 | 51 | function setEmergencyAdmin(address admin) external; 52 | 53 | function getPriceOracle() external view returns (address); 54 | 55 | function setPriceOracle(address priceOracle) external; 56 | 57 | function getLendingRateOracle() external view returns (address); 58 | 59 | function setLendingRateOracle(address lendingRateOracle) external; 60 | } 61 | -------------------------------------------------------------------------------- /test/compound/03_prices.test.ts: -------------------------------------------------------------------------------- 1 | import { CTokenInterface } from "../../typechain"; 2 | import { compoundFixture } from "./utils/fixtures"; 3 | import { waffle } from "hardhat"; 4 | import { parseUnits } from "@ethersproject/units"; 5 | 6 | describe("Compound: Statistics, Exchange Rate, etc", function () { 7 | let cDAI: CTokenInterface; 8 | 9 | before(async function () { 10 | ({ cDAI } = await waffle.loadFixture(compoundFixture)); 11 | }); 12 | 13 | it("Compound: gets the exchange rate", async () => { 14 | /* 15 | according to compound docs, The current exchange rate as an unsigned integer, 16 | scaled by 1 * 10^(18 - 8 + Underlying Token Decimals). 17 | 18 | So DAI is 18 decimals, hence the exchangeRate is scaled by: 19 | 10^(18-8+18) = 10^(28) 20 | 21 | So to calculate how many cDAI come in 1 DAI, we need to scale this 28 unit number 22 | into 18 unit number, since DAI is also 18 Unit and then we can calculate easily 23 | */ 24 | 25 | // convert the 10^28 unit exchangeRate to 10^18 26 | const exchangeRate = (await cDAI.exchangeRateStored()).div(parseUnits("1", 10)); 27 | console.log("The exchange rate is incoming ➡️"); 28 | console.log("1 DAI = ", parseUnits("1", 18).div(exchangeRate).toString(), " cDAI"); 29 | console.log("1 CDAI = ", 1 / parseUnits("1", 18).div(exchangeRate).toNumber(), " DAI"); 30 | }); 31 | 32 | it("Compound: check how many underlying tokens the cToken contract holds", async () => { 33 | const cash = await cDAI.callStatic.getCash(); 34 | console.log("The cDAI contract holds over ", cash.toString(), " DAI"); 35 | }); 36 | 37 | it("Compound: check total borrows and total supply of the underlying token", async () => { 38 | const totalBorrow = await cDAI.callStatic.totalBorrowsCurrent(); 39 | const totalSupply = await cDAI.callStatic.totalSupply(); 40 | console.log("total borrow of DAI = ", totalBorrow.toString()); 41 | console.log("total supply of DAI = ", totalSupply.toString()); 42 | }); 43 | 44 | it("Compound: calculating borrow and supply APY", async () => { 45 | const blocksPerDay = 6570; // 13.15 secs per block 46 | const daysPerYear = 365; 47 | 48 | // supply rate divided by eth mantissa 49 | const supplyRate = (await cDAI.supplyRatePerBlock()).toNumber() / 1e18; 50 | const supplyAPY = 100 * (Math.pow(supplyRate * blocksPerDay + 1, daysPerYear) - 1); 51 | 52 | const borrowRate = (await cDAI.borrowRatePerBlock()).toNumber() / 1e18; 53 | const borrowAPY = 100 * (Math.pow(borrowRate * blocksPerDay + 1, daysPerYear) - 1); 54 | 55 | console.log("SupplyAPY of DAI = ", supplyAPY); 56 | console.log("BorrowAPY of DAI = ", borrowAPY); 57 | }); 58 | }); 59 | -------------------------------------------------------------------------------- /test/aave_v2/03_stats.test.ts: -------------------------------------------------------------------------------- 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("Aave V2: 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("Aave V2: 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("Aave V2: 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("Aave V2: 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 | }); 58 | -------------------------------------------------------------------------------- /hardhat.config.ts: -------------------------------------------------------------------------------- 1 | import "@nomiclabs/hardhat-waffle"; 2 | import "@typechain/hardhat"; 3 | import "hardhat-gas-reporter"; 4 | import "solidity-coverage"; 5 | 6 | import "./tasks/accounts"; 7 | import "./tasks/clean"; 8 | 9 | import { resolve } from "path"; 10 | 11 | import { config as dotenvConfig } from "dotenv"; 12 | import { HardhatUserConfig } from "hardhat/config"; 13 | import { NetworkUserConfig } from "hardhat/types"; 14 | 15 | dotenvConfig({ path: resolve(__dirname, "./.env") }); 16 | 17 | const chainIds = { 18 | ganache: 1337, 19 | goerli: 5, 20 | hardhat: 31337, 21 | kovan: 42, 22 | mainnet: 1, 23 | rinkeby: 4, 24 | ropsten: 3, 25 | }; 26 | 27 | // Ensure that we have all the environment variables we need. 28 | const mnemonic = process.env.MNEMONIC; 29 | if (!mnemonic) { 30 | throw new Error("Please set your MNEMONIC in a .env file"); 31 | } 32 | 33 | const alchemyApiKey = process.env.ALCHEMY_API_KEY; 34 | if (!alchemyApiKey) { 35 | throw new Error("Please set your ALCHEMY_API_KEY in a .env file"); 36 | } 37 | 38 | function createTestnetConfig(network: keyof typeof chainIds): NetworkUserConfig { 39 | const url: string = `https://eth-${network}.alchemyapi.io/v2/${alchemyApiKey}`; 40 | return { 41 | accounts: { 42 | count: 10, 43 | initialIndex: 0, 44 | mnemonic, 45 | path: "m/44'/60'/0'/0", 46 | }, 47 | chainId: chainIds[network], 48 | url, 49 | }; 50 | } 51 | 52 | const config: HardhatUserConfig = { 53 | defaultNetwork: "hardhat", 54 | gasReporter: { 55 | currency: "USD", 56 | enabled: process.env.REPORT_GAS ? true : false, 57 | excludeContracts: [], 58 | src: "./contracts", 59 | }, 60 | networks: { 61 | hardhat: { 62 | accounts: { 63 | mnemonic, 64 | }, 65 | chainId: chainIds.hardhat, 66 | forking: { 67 | url: `http://eth-mainnet.alchemyapi.io/v2/${alchemyApiKey}`, 68 | blockNumber: 12570127, 69 | }, 70 | }, 71 | goerli: createTestnetConfig("goerli"), 72 | kovan: createTestnetConfig("kovan"), 73 | rinkeby: createTestnetConfig("rinkeby"), 74 | ropsten: createTestnetConfig("ropsten"), 75 | }, 76 | paths: { 77 | artifacts: "./artifacts", 78 | cache: "./cache", 79 | sources: "./contracts", 80 | tests: "./test", 81 | }, 82 | solidity: { 83 | version: "0.8.4", 84 | settings: { 85 | metadata: { 86 | // Not including the metadata hash 87 | // https://github.com/paulrberg/solidity-template/issues/31 88 | bytecodeHash: "none", 89 | }, 90 | // You should disable the optimizer when debugging 91 | // https://hardhat.org/hardhat-network/#solidity-optimizer-support 92 | optimizer: { 93 | enabled: true, 94 | runs: 800, 95 | }, 96 | }, 97 | }, 98 | typechain: { 99 | outDir: "typechain", 100 | target: "ethers-v5", 101 | }, 102 | mocha: { 103 | timeout: 100 * 1000, 104 | }, 105 | }; 106 | 107 | export default config; 108 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "defi-playground", 3 | "description": "DeFi protocols playground!", 4 | "version": "1.0.0", 5 | "author": { 6 | "name": "Daksh Miglani", 7 | "url": "https://dak.sh" 8 | }, 9 | "devDependencies": { 10 | "@codechecks/client": "^0.1.10", 11 | "@commitlint/cli": "^12.1.4", 12 | "@commitlint/config-conventional": "^12.1.4", 13 | "@ethersproject/abi": "^5.1.2", 14 | "@ethersproject/abstract-signer": "^5.1.0", 15 | "@ethersproject/bignumber": "^5.1.1", 16 | "@ethersproject/bytes": "^5.1.0", 17 | "@ethersproject/contracts": "^5.2.0", 18 | "@ethersproject/providers": "^5.1.2", 19 | "@nomiclabs/hardhat-ethers": "^2.0.2", 20 | "@nomiclabs/hardhat-waffle": "^2.0.1", 21 | "@openzeppelin/contracts": "^4.1.0", 22 | "@typechain/ethers-v5": "^7.0.0", 23 | "@typechain/hardhat": "^2.0.1", 24 | "@types/chai": "^4.2.18", 25 | "@types/chai-as-promised": "^7.1.4", 26 | "@types/fs-extra": "^9.0.11", 27 | "@types/mocha": "^8.2.2", 28 | "@types/node": "^15.3.0", 29 | "@typescript-eslint/eslint-plugin": "^4.23.0", 30 | "@typescript-eslint/parser": "^4.23.0", 31 | "chai": "^4.3.4", 32 | "chai-as-promised": "^7.1.1", 33 | "commitizen": "^4.2.4", 34 | "cz-conventional-changelog": "^3.3.0", 35 | "dotenv": "^9.0.2", 36 | "eslint": "^7.26.0", 37 | "eslint-config-prettier": "^8.3.0", 38 | "ethereum-waffle": "^3.3.0", 39 | "ethers": "^5.1.4", 40 | "fs-extra": "^10.0.0", 41 | "hardhat": "^2.3.0", 42 | "hardhat-gas-reporter": "^1.0.4", 43 | "husky": "^6.0.0", 44 | "lint-staged": "^11.0.0", 45 | "mocha": "^8.4.0", 46 | "prettier": "^2.3.0", 47 | "prettier-plugin-solidity": "^1.0.0-beta.10", 48 | "shelljs": "^0.8.4", 49 | "solhint": "^3.3.4", 50 | "solhint-plugin-prettier": "^0.0.5", 51 | "solidity-coverage": "^0.7.16", 52 | "ts-generator": "^0.1.1", 53 | "ts-node": "^9.1.1", 54 | "typechain": "^5.0.0", 55 | "typescript": "^4.2.4" 56 | }, 57 | "files": [ 58 | "/contracts" 59 | ], 60 | "keywords": [ 61 | "blockchain", 62 | "ethereum", 63 | "hardhat", 64 | "smart-contracts", 65 | "solidity" 66 | ], 67 | "license": "WTFPL", 68 | "publishConfig": { 69 | "access": "public" 70 | }, 71 | "lint-staged": { 72 | "*.{js,json,md,sol,ts}": [ 73 | "prettier --config .prettierrc --write" 74 | ] 75 | }, 76 | "scripts": { 77 | "postinstall": "husky install", 78 | "clean": "hardhat clean", 79 | "commit": "git-cz", 80 | "compile": "hardhat compile", 81 | "coverage": "hardhat coverage --solcoverjs ./.solcover.js --temp artifacts --testfiles \"./test/**/*.ts\"", 82 | "lint": "yarn run lint:sol && yarn run lint:ts && yarn run prettier:check", 83 | "lint:sol": "solhint --config ./.solhint.json --max-warnings 0 \"contracts/**/*.sol\"", 84 | "lint:ts": "eslint --config ./.eslintrc.yaml --ignore-path ./.eslintignore --ext .js,.ts .", 85 | "prettier": "prettier --config .prettierrc --write \"**/*.{js,json,md,sol,ts}\"", 86 | "prettier:check": "prettier --check --config .prettierrc \"**/*.{js,json,md,sol,ts}\"", 87 | "test": "hardhat test", 88 | "typechain": "hardhat typechain" 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /contracts/aave_v2/IAToken.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.0; 2 | 3 | import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 4 | import { IScaledBalanceToken } from "./IScaledBalanceToken.sol"; 5 | 6 | interface IAToken is IERC20, IScaledBalanceToken { 7 | /** 8 | * @dev Emitted after the mint action 9 | * @param from The address performing the mint 10 | * @param value The amount being 11 | * @param index The new liquidity index of the reserve 12 | **/ 13 | event Mint(address indexed from, uint256 value, uint256 index); 14 | 15 | /** 16 | * @dev Mints `amount` aTokens to `user` 17 | * @param user The address receiving the minted tokens 18 | * @param amount The amount of tokens getting minted 19 | * @param index The new liquidity index of the reserve 20 | * @return `true` if the the previous balance of the user was 0 21 | */ 22 | function mint( 23 | address user, 24 | uint256 amount, 25 | uint256 index 26 | ) external returns (bool); 27 | 28 | /** 29 | * @dev Emitted after aTokens are burned 30 | * @param from The owner of the aTokens, getting them burned 31 | * @param target The address that will receive the underlying 32 | * @param value The amount being burned 33 | * @param index The new liquidity index of the reserve 34 | **/ 35 | event Burn(address indexed from, address indexed target, uint256 value, uint256 index); 36 | 37 | /** 38 | * @dev Emitted during the transfer action 39 | * @param from The user whose tokens are being transferred 40 | * @param to The recipient 41 | * @param value The amount being transferred 42 | * @param index The new liquidity index of the reserve 43 | **/ 44 | event BalanceTransfer(address indexed from, address indexed to, uint256 value, uint256 index); 45 | 46 | /** 47 | * @dev Burns aTokens from `user` and sends the equivalent amount of underlying to `receiverOfUnderlying` 48 | * @param user The owner of the aTokens, getting them burned 49 | * @param receiverOfUnderlying The address that will receive the underlying 50 | * @param amount The amount being burned 51 | * @param index The new liquidity index of the reserve 52 | **/ 53 | function burn( 54 | address user, 55 | address receiverOfUnderlying, 56 | uint256 amount, 57 | uint256 index 58 | ) external; 59 | 60 | /** 61 | * @dev Mints aTokens to the reserve treasury 62 | * @param amount The amount of tokens getting minted 63 | * @param index The new liquidity index of the reserve 64 | */ 65 | function mintToTreasury(uint256 amount, uint256 index) external; 66 | 67 | /** 68 | * @dev Transfers aTokens in the event of a borrow being liquidated, in case the liquidators reclaims the aToken 69 | * @param from The address getting liquidated, current owner of the aTokens 70 | * @param to The recipient 71 | * @param value The amount of tokens getting transferred 72 | **/ 73 | function transferOnLiquidation( 74 | address from, 75 | address to, 76 | uint256 value 77 | ) external; 78 | 79 | /** 80 | * @dev Transfers the underlying asset to `target`. Used by the LendingPool to transfer 81 | * assets in borrow(), withdraw() and flashLoan() 82 | * @param user The recipient of the aTokens 83 | * @param amount The amount getting transferred 84 | * @return The amount transferred 85 | **/ 86 | function transferUnderlyingTo(address user, uint256 amount) external returns (uint256); 87 | } 88 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 |

DeFi Playground 📈😱🚀

5 | 6 | 🙋‍♂️ I made this repo to teach myself, "how to do x with y" where "y" is a DeFi protocol. 7 | 8 | ## Requirements 9 | 10 | 1. Node.js v14+ 11 | 2. [Alchemy](http://alchemyapi.io/) - Make an alchemy account and set the `ALCHEMY_API_KEY` environment variable in the .env file 12 | 3. Run `yarn` to install dependencies 13 | 4. Run `yarn test` to test the implementations 14 | 15 | ## [Compound Finance](https://compound.finance/) 16 | 17 | ### Helpful Reading 18 | 19 | - 💽 [Interfaces](./contracts/compound) we use to interact with Compound's contracts 20 | - # [Addresses](./test/compound/utils/consts.ts#L1) we use to connect to the mainnet deployed contracts 21 | - ⚡️ Check [Fixtures](./test/compound/utils/fixtures.ts#L25), we use these to setup our tests 22 | - 📜 Compound Docs for [cTokens](https://compound.finance/docs/ctokens), [Comptroller, ie: controller](https://compound.finance/docs/comptroller) 23 | 24 | ### 🤔 How tos? 25 | 26 | 1. 💰 [How to `deposit` a token and get back a interest bearing cTokens?](./test/compound/01_deposits.test.ts#L19) 27 | 2. 💸 [How to `withdraw` / `cash out` your tokens by returning cTokens?](./test/compound/01_deposits.test.ts#L36) 28 | 3. 🤔 [How to `earn interest` on your token deposits? (Demonstration)](./test/compound/01_deposits.test.ts#L65) 29 | 4. 🏦 [How to take a `loan` and borrow tokens after setting a collateral?](./test/compound/02_borrow.test.ts#L22) 30 | 5. ⚖️ [How to check the balance you `borrowed`?](./test/compound/02_borrow.test.ts#L40) 31 | 6. 🥳 [How to `repay` the loan?](./test/compound/02_borrow.test.ts#L45) 32 | 7. 📈 [How to calculate the `exchange rate` of cTokens?](./test/compound/03_prices.test.ts#L20) 33 | 8. 📦 [How many `underlying tokens` does the compound contract holds?](./test/compound/03_prices.test.ts#L39) 34 | 9. 🧐 [How to check the `total supply` and `total borrows` of a token?](./test/compound/03_prices.test.ts#L44) 35 | 10. 🙋‍♂️ [How to calculate the `supply APY` and `borrow APR` of a token?](./test/compound/03_prices.test.ts#L51) 36 | 37 | ## [Aave V2](https://app.aave.com/markets) 38 | 39 | ### Helpful Reading 40 | 41 | - 💽 [Interfaces](./contracts/aave_v2/) we use to interact with Aave V2 contracts 42 | - # [Addresses](./test/aave_v2/utils/consts.ts#L1) we use to connect to the mainnet deployed contracts 43 | - ⚡️ Check [Fixtures](./test/aave_v2/utils/fixtures.ts#L25), we use these to setup our tests 44 | - 📜 Aave V2 Docs for [Lending Pool](https://docs.aave.com/developers/the-core-protocol/lendingpool), [Price Oracle](https://docs.aave.com/developers/the-core-protocol/price-oracle), [aTokens](https://docs.aave.com/developers/the-core-protocol/atokens) and [FAQs](https://docs.aave.com/developers/glossary) 45 | 46 | ### 🤔 How tos? 47 | 48 | 1. 💰 [How to `deposit erc20 token` and get back interest bearing `aTokens`?](./test/aave_v2/01_deposits.test.ts#L20) 49 | 2. 💸 [How to `withdraw/cashout erc20 tokens` by returning back `aTokens`?](./test/aave_v2/01_deposits.test.ts#L45) 50 | 3. 🤔 [How to `earn interest` on your `erc20 token` deposits?](./test/aave_v2/01_deposits.test.ts#L74) 51 | 4. 🏦 [How to set a particular `erc20 token` as collateral for your loan borrows?](./test/aave_v2/02_borrow.test.ts#L26) 52 | 5. 💵 [How to take a `stable interest rate loan` against your collateral?](./test/aave_v2/02_borrow.test.ts#L37) 53 | 6. 💱 [How to convert your `stable interest loan` to a `variable interest loan`?](./test/aave_v2/02_borrow.test.ts#L63) 54 | 7. 🥳 [How to `repay` the loan?](./test/aave_v2/02_borrow.test.ts#L72) 55 | 8. 🧐 [How to get `user data` (ie: total collateral, debt, amount borrowable)?](./test/aave_v2/03_stats.test.ts#L28) 56 | 9. 🧐 [How to get `user data` (ie: total collateral, debt, amount borrowable)?](./test/aave_v2/03_stats.test.ts#L28) 57 | 10. 🙋‍♂️ [How to get the `supply APY` and `borrow APR` for a `erc20 token`](./test/aave_v2/03_stats.test.ts#L38) 58 | 11. ⚖️ [How to get the `price of erc20 token` in `ETH` from aave oracle?](./test/aave_v2/03_stats.test.ts#L51) 59 | 12. TBD - Flashloans 60 | -------------------------------------------------------------------------------- /test/aave_v2/01_deposits.test.ts: -------------------------------------------------------------------------------- 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("Aave V2: 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("Aave V2: 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.gte(amt.sub(1)); // expect the amount to be lower due to roundoff 43 | }); 44 | 45 | it("Aave V2: 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("Aave V2: earn interest", async () => { 75 | // let's get some aDAI first 76 | const amt = await DAI.balanceOf(wallet.address); 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 | await 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 | }); 94 | -------------------------------------------------------------------------------- /test/aave_v2/02_borrow.test.ts: -------------------------------------------------------------------------------- 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("Aave V2: 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("Aave V2: 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("Aave V2: 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("Aave V2: 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("Aave V2: repay the borrowed sum (ie: repay 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 | }); 93 | -------------------------------------------------------------------------------- /test/compound/01_deposits.test.ts: -------------------------------------------------------------------------------- 1 | import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; 2 | import { CTokenInterface, ERC20 } from "../../typechain"; 3 | import { mineBlocks, useChai } from "../utils"; 4 | import { compoundFixture } from "./utils/fixtures"; 5 | import { waffle } from "hardhat"; 6 | const expect = useChai(); 7 | 8 | describe("Compound: Deposit & Redeem", function () { 9 | let wallet: SignerWithAddress; 10 | let USDC: ERC20; 11 | let cUSDC: CTokenInterface; 12 | 13 | before(async function () { 14 | // go read the code for this fixture to understand 15 | // how we get the wallet, erc20 USDC token, ctoken of USDC and etc. 16 | ({ wallet, USDC, cUSDC } = await waffle.loadFixture(compoundFixture)); 17 | }); 18 | 19 | it("Compound: deposit USDC, get cUSDC", async () => { 20 | // approve the request for cUSDC to transfer 1000 USDC from your account 21 | await USDC.approve(cUSDC.address, 1000); 22 | // mint "x" cUSDC by giving them 1000 USDC 23 | await cUSDC.mint(1000); 24 | 25 | console.log("AMT USDC Minted:", (await cUSDC.balanceOf(wallet.address)).toString()); 26 | 27 | // amt minted must not be 0 28 | expect(await cUSDC.balanceOf(wallet.address)).not.eq(0); 29 | // balance of underlying should be pretty close to 1000 (compound has a roundoff bug) 30 | // also since, balanceOfUnderlying is `not` a view function, we need to use callStatic 31 | // if you don't call it with callStatic it will `accrue` the interest in the output 32 | // check this: https://github.com/compound-finance/compound-protocol/blob/b9b14038612d846b83f8a009a82c38974ff2dcfe/contracts/CToken.sol#L190 33 | expect(await cUSDC.callStatic.balanceOfUnderlying(wallet.address)).to.eq(999); 34 | }); 35 | 36 | it("Compound: cashes USDC out by returning cUSDC", async () => { 37 | /* there are two ways of cashing out our tokens 38 | 1. by the amount of USDC we want to cash out 39 | 2. by the amount of cUSDC we want to cash out 40 | 41 | the amount of those can be different based on exchange rates overtime 42 | which is why we have two functions. 43 | */ 44 | 45 | // 1. cash out based on number of ctokens 46 | // let's check the amount of USDC and cUSDC we have 47 | let usdcBalance = await USDC.balanceOf(wallet.address); 48 | const currentTokens = await cUSDC.balanceOf(wallet.address); 49 | // redeem 1/3rd of the cTokens 50 | await cUSDC.redeem(currentTokens.div(3)); 51 | 52 | // we should get back 1/3rd of the underlying USDC 53 | expect(await USDC.balanceOf(wallet.address)).to.eq(usdcBalance.add(999 / 3)); 54 | // we should have remaining 2/3rd of the total cTokens 55 | expect(await cUSDC.balanceOf(wallet.address)).to.eq(currentTokens.sub(currentTokens.div(3))); 56 | 57 | usdcBalance = await USDC.balanceOf(wallet.address); 58 | 59 | // 2. cash out based on the number of underlying USDC we have 60 | const underlyingUSDC = await cUSDC.callStatic.balanceOfUnderlying(wallet.address); 61 | await cUSDC.redeemUnderlying(underlyingUSDC); 62 | expect(await USDC.balanceOf(wallet.address)).to.eq(usdcBalance.add(underlyingUSDC)); 63 | }); 64 | 65 | it("Compound: earn interest on your deposits", async () => { 66 | /* 67 | the past 2 functions demonstrated how you can deposit and reedem your collateral 68 | as well as basics of cTokens, now let's earn interest. 69 | Compound gives interest `not` by increasing the number of cTokens but instead 70 | by increasing the value of those cTokens every block. 71 | 72 | So the amount of USDC you get after holding cUSDC for a 1000s of blocks will be 73 | higher than the original deposit. 74 | */ 75 | 76 | // so let's get some cTokens 77 | await USDC.approve(cUSDC.address, 5000); 78 | await cUSDC.mint(5000); 79 | let bal = await cUSDC.callStatic.balanceOfUnderlying(wallet.address); 80 | console.log("after depositing balance is: ", bal.toString(), " USDC"); 81 | expect(bal).to.eq(5000); 82 | 83 | // now let's mine a large chunk of blocks and accrue some interest 84 | await mineBlocks(10000); 85 | // after mining blocks, we need to call balanceOfUnderlying `without` callStatic 86 | // because it will accrue our interest earned in the duration of these blocks 87 | await (await cUSDC.balanceOfUnderlying(wallet.address)).wait(); 88 | 89 | // let's get our balance back again 90 | bal = await cUSDC.callStatic.balanceOfUnderlying(wallet.address); 91 | console.log("balance after accruing interest and 10K blocks: ", bal.toString(), " USDC"); 92 | expect(bal).to.gt(5000); 93 | }); 94 | }); 95 | -------------------------------------------------------------------------------- /contracts/aave_v2/ILendingPool.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: agpl-3.0 2 | pragma solidity ^0.8.0; 3 | 4 | import { ILendingPoolAddressesProvider } from "./ILendingPoolAddressesProvider.sol"; 5 | import { DataTypes } from "./DataTypes.sol"; 6 | 7 | interface ILendingPool { 8 | /** 9 | * @dev Emitted on deposit() 10 | * @param reserve The address of the underlying asset of the reserve 11 | * @param user The address initiating the deposit 12 | * @param onBehalfOf The beneficiary of the deposit, receiving the aTokens 13 | * @param amount The amount deposited 14 | * @param referral The referral code used 15 | **/ 16 | event Deposit( 17 | address indexed reserve, 18 | address user, 19 | address indexed onBehalfOf, 20 | uint256 amount, 21 | uint16 indexed referral 22 | ); 23 | 24 | /** 25 | * @dev Emitted on withdraw() 26 | * @param reserve The address of the underlyng asset being withdrawn 27 | * @param user The address initiating the withdrawal, owner of aTokens 28 | * @param to Address that will receive the underlying 29 | * @param amount The amount to be withdrawn 30 | **/ 31 | event Withdraw(address indexed reserve, address indexed user, address indexed to, uint256 amount); 32 | 33 | /** 34 | * @dev Emitted on borrow() and flashLoan() when debt needs to be opened 35 | * @param reserve The address of the underlying asset being borrowed 36 | * @param user The address of the user initiating the borrow(), receiving the funds on borrow() or just 37 | * initiator of the transaction on flashLoan() 38 | * @param onBehalfOf The address that will be getting the debt 39 | * @param amount The amount borrowed out 40 | * @param borrowRateMode The rate mode: 1 for Stable, 2 for Variable 41 | * @param borrowRate The numeric rate at which the user has borrowed 42 | * @param referral The referral code used 43 | **/ 44 | event Borrow( 45 | address indexed reserve, 46 | address user, 47 | address indexed onBehalfOf, 48 | uint256 amount, 49 | uint256 borrowRateMode, 50 | uint256 borrowRate, 51 | uint16 indexed referral 52 | ); 53 | 54 | /** 55 | * @dev Emitted on repay() 56 | * @param reserve The address of the underlying asset of the reserve 57 | * @param user The beneficiary of the repayment, getting his debt reduced 58 | * @param repayer The address of the user initiating the repay(), providing the funds 59 | * @param amount The amount repaid 60 | **/ 61 | event Repay(address indexed reserve, address indexed user, address indexed repayer, uint256 amount); 62 | 63 | /** 64 | * @dev Emitted on swapBorrowRateMode() 65 | * @param reserve The address of the underlying asset of the reserve 66 | * @param user The address of the user swapping his rate mode 67 | * @param rateMode The rate mode that the user wants to swap to 68 | **/ 69 | event Swap(address indexed reserve, address indexed user, uint256 rateMode); 70 | 71 | /** 72 | * @dev Emitted on setUserUseReserveAsCollateral() 73 | * @param reserve The address of the underlying asset of the reserve 74 | * @param user The address of the user enabling the usage as collateral 75 | **/ 76 | event ReserveUsedAsCollateralEnabled(address indexed reserve, address indexed user); 77 | 78 | /** 79 | * @dev Emitted on setUserUseReserveAsCollateral() 80 | * @param reserve The address of the underlying asset of the reserve 81 | * @param user The address of the user enabling the usage as collateral 82 | **/ 83 | event ReserveUsedAsCollateralDisabled(address indexed reserve, address indexed user); 84 | 85 | /** 86 | * @dev Emitted on rebalanceStableBorrowRate() 87 | * @param reserve The address of the underlying asset of the reserve 88 | * @param user The address of the user for which the rebalance has been executed 89 | **/ 90 | event RebalanceStableBorrowRate(address indexed reserve, address indexed user); 91 | 92 | /** 93 | * @dev Emitted on flashLoan() 94 | * @param target The address of the flash loan receiver contract 95 | * @param initiator The address initiating the flash loan 96 | * @param asset The address of the asset being flash borrowed 97 | * @param amount The amount flash borrowed 98 | * @param premium The fee flash borrowed 99 | * @param referralCode The referral code used 100 | **/ 101 | event FlashLoan( 102 | address indexed target, 103 | address indexed initiator, 104 | address indexed asset, 105 | uint256 amount, 106 | uint256 premium, 107 | uint16 referralCode 108 | ); 109 | 110 | /** 111 | * @dev Emitted when the pause is triggered. 112 | */ 113 | event Paused(); 114 | 115 | /** 116 | * @dev Emitted when the pause is lifted. 117 | */ 118 | event Unpaused(); 119 | 120 | /** 121 | * @dev Emitted when a borrower is liquidated. This event is emitted by the LendingPool via 122 | * LendingPoolCollateral manager using a DELEGATECALL 123 | * This allows to have the events in the generated ABI for LendingPool. 124 | * @param collateralAsset The address of the underlying asset used as collateral, to receive as result of the liquidation 125 | * @param debtAsset The address of the underlying borrowed asset to be repaid with the liquidation 126 | * @param user The address of the borrower getting liquidated 127 | * @param debtToCover The debt amount of borrowed `asset` the liquidator wants to cover 128 | * @param liquidatedCollateralAmount The amount of collateral received by the liiquidator 129 | * @param liquidator The address of the liquidator 130 | * @param receiveAToken `true` if the liquidators wants to receive the collateral aTokens, `false` if he wants 131 | * to receive the underlying collateral asset directly 132 | **/ 133 | event LiquidationCall( 134 | address indexed collateralAsset, 135 | address indexed debtAsset, 136 | address indexed user, 137 | uint256 debtToCover, 138 | uint256 liquidatedCollateralAmount, 139 | address liquidator, 140 | bool receiveAToken 141 | ); 142 | 143 | /** 144 | * @dev Emitted when the state of a reserve is updated. NOTE: This event is actually declared 145 | * in the ReserveLogic library and emitted in the updateInterestRates() function. Since the function is internal, 146 | * the event will actually be fired by the LendingPool contract. The event is therefore replicated here so it 147 | * gets added to the LendingPool ABI 148 | * @param reserve The address of the underlying asset of the reserve 149 | * @param liquidityRate The new liquidity rate 150 | * @param stableBorrowRate The new stable borrow rate 151 | * @param variableBorrowRate The new variable borrow rate 152 | * @param liquidityIndex The new liquidity index 153 | * @param variableBorrowIndex The new variable borrow index 154 | **/ 155 | event ReserveDataUpdated( 156 | address indexed reserve, 157 | uint256 liquidityRate, 158 | uint256 stableBorrowRate, 159 | uint256 variableBorrowRate, 160 | uint256 liquidityIndex, 161 | uint256 variableBorrowIndex 162 | ); 163 | 164 | /** 165 | * @dev Deposits an `amount` of underlying asset into the reserve, receiving in return overlying aTokens. 166 | * - E.g. User deposits 100 USDC and gets in return 100 aUSDC 167 | * @param asset The address of the underlying asset to deposit 168 | * @param amount The amount to be deposited 169 | * @param onBehalfOf The address that will receive the aTokens, same as msg.sender if the user 170 | * wants to receive them on his own wallet, or a different address if the beneficiary of aTokens 171 | * is a different wallet 172 | * @param referralCode Code used to register the integrator originating the operation, for potential rewards. 173 | * 0 if the action is executed directly by the user, without any middle-man 174 | **/ 175 | function deposit( 176 | address asset, 177 | uint256 amount, 178 | address onBehalfOf, 179 | uint16 referralCode 180 | ) external; 181 | 182 | /** 183 | * @dev Withdraws an `amount` of underlying asset from the reserve, burning the equivalent aTokens owned 184 | * E.g. User has 100 aUSDC, calls withdraw() and receives 100 USDC, burning the 100 aUSDC 185 | * @param asset The address of the underlying asset to withdraw 186 | * @param amount The underlying amount to be withdrawn 187 | * - Send the value type(uint256).max in order to withdraw the whole aToken balance 188 | * @param to Address that will receive the underlying, same as msg.sender if the user 189 | * wants to receive it on his own wallet, or a different address if the beneficiary is a 190 | * different wallet 191 | * @return The final amount withdrawn 192 | **/ 193 | function withdraw( 194 | address asset, 195 | uint256 amount, 196 | address to 197 | ) external returns (uint256); 198 | 199 | /** 200 | * @dev Allows users to borrow a specific `amount` of the reserve underlying asset, provided that the borrower 201 | * already deposited enough collateral, or he was given enough allowance by a credit delegator on the 202 | * corresponding debt token (StableDebtToken or VariableDebtToken) 203 | * - E.g. User borrows 100 USDC passing as `onBehalfOf` his own address, receiving the 100 USDC in his wallet 204 | * and 100 stable/variable debt tokens, depending on the `interestRateMode` 205 | * @param asset The address of the underlying asset to borrow 206 | * @param amount The amount to be borrowed 207 | * @param interestRateMode The interest rate mode at which the user wants to borrow: 1 for Stable, 2 for Variable 208 | * @param referralCode Code used to register the integrator originating the operation, for potential rewards. 209 | * 0 if the action is executed directly by the user, without any middle-man 210 | * @param onBehalfOf Address of the user who will receive the debt. Should be the address of the borrower itself 211 | * calling the function if he wants to borrow against his own collateral, or the address of the credit delegator 212 | * if he has been given credit delegation allowance 213 | **/ 214 | function borrow( 215 | address asset, 216 | uint256 amount, 217 | uint256 interestRateMode, 218 | uint16 referralCode, 219 | address onBehalfOf 220 | ) external; 221 | 222 | /** 223 | * @notice Repays a borrowed `amount` on a specific reserve, burning the equivalent debt tokens owned 224 | * - E.g. User repays 100 USDC, burning 100 variable/stable debt tokens of the `onBehalfOf` address 225 | * @param asset The address of the borrowed underlying asset previously borrowed 226 | * @param amount The amount to repay 227 | * - Send the value type(uint256).max in order to repay the whole debt for `asset` on the specific `debtMode` 228 | * @param rateMode The interest rate mode at of the debt the user wants to repay: 1 for Stable, 2 for Variable 229 | * @param onBehalfOf Address of the user who will get his debt reduced/removed. Should be the address of the 230 | * user calling the function if he wants to reduce/remove his own debt, or the address of any other 231 | * other borrower whose debt should be removed 232 | * @return The final amount repaid 233 | **/ 234 | function repay( 235 | address asset, 236 | uint256 amount, 237 | uint256 rateMode, 238 | address onBehalfOf 239 | ) external returns (uint256); 240 | 241 | /** 242 | * @dev Allows a borrower to swap his debt between stable and variable mode, or viceversa 243 | * @param asset The address of the underlying asset borrowed 244 | * @param rateMode The rate mode that the user wants to swap to 245 | **/ 246 | function swapBorrowRateMode(address asset, uint256 rateMode) external; 247 | 248 | /** 249 | * @dev Rebalances the stable interest rate of a user to the current stable rate defined on the reserve. 250 | * - Users can be rebalanced if the following conditions are satisfied: 251 | * 1. Usage ratio is above 95% 252 | * 2. the current deposit APY is below REBALANCE_UP_THRESHOLD * maxVariableBorrowRate, which means that too much has been 253 | * borrowed at a stable rate and depositors are not earning enough 254 | * @param asset The address of the underlying asset borrowed 255 | * @param user The address of the user to be rebalanced 256 | **/ 257 | function rebalanceStableBorrowRate(address asset, address user) external; 258 | 259 | /** 260 | * @dev Allows depositors to enable/disable a specific deposited asset as collateral 261 | * @param asset The address of the underlying asset deposited 262 | * @param useAsCollateral `true` if the user wants to use the deposit as collateral, `false` otherwise 263 | **/ 264 | function setUserUseReserveAsCollateral(address asset, bool useAsCollateral) external; 265 | 266 | /** 267 | * @dev Function to liquidate a non-healthy position collateral-wise, with Health Factor below 1 268 | * - The caller (liquidator) covers `debtToCover` amount of debt of the user getting liquidated, and receives 269 | * a proportionally amount of the `collateralAsset` plus a bonus to cover market risk 270 | * @param collateralAsset The address of the underlying asset used as collateral, to receive as result of the liquidation 271 | * @param debtAsset The address of the underlying borrowed asset to be repaid with the liquidation 272 | * @param user The address of the borrower getting liquidated 273 | * @param debtToCover The debt amount of borrowed `asset` the liquidator wants to cover 274 | * @param receiveAToken `true` if the liquidators wants to receive the collateral aTokens, `false` if he wants 275 | * to receive the underlying collateral asset directly 276 | **/ 277 | function liquidationCall( 278 | address collateralAsset, 279 | address debtAsset, 280 | address user, 281 | uint256 debtToCover, 282 | bool receiveAToken 283 | ) external; 284 | 285 | /** 286 | * @dev Allows smartcontracts to access the liquidity of the pool within one transaction, 287 | * as long as the amount taken plus a fee is returned. 288 | * IMPORTANT There are security concerns for developers of flashloan receiver contracts that must be kept into consideration. 289 | * For further details please visit https://developers.aave.com 290 | * @param receiverAddress The address of the contract receiving the funds, implementing the IFlashLoanReceiver interface 291 | * @param assets The addresses of the assets being flash-borrowed 292 | * @param amounts The amounts amounts being flash-borrowed 293 | * @param modes Types of the debt to open if the flash loan is not returned: 294 | * 0 -> Don't open any debt, just revert if funds can't be transferred from the receiver 295 | * 1 -> Open debt at stable rate for the value of the amount flash-borrowed to the `onBehalfOf` address 296 | * 2 -> Open debt at variable rate for the value of the amount flash-borrowed to the `onBehalfOf` address 297 | * @param onBehalfOf The address that will receive the debt in the case of using on `modes` 1 or 2 298 | * @param params Variadic packed params to pass to the receiver as extra information 299 | * @param referralCode Code used to register the integrator originating the operation, for potential rewards. 300 | * 0 if the action is executed directly by the user, without any middle-man 301 | **/ 302 | function flashLoan( 303 | address receiverAddress, 304 | address[] calldata assets, 305 | uint256[] calldata amounts, 306 | uint256[] calldata modes, 307 | address onBehalfOf, 308 | bytes calldata params, 309 | uint16 referralCode 310 | ) external; 311 | 312 | /** 313 | * @dev Returns the user account data across all the reserves 314 | * @param user The address of the user 315 | * @return totalCollateralETH the total collateral in ETH of the user 316 | * @return totalDebtETH the total debt in ETH of the user 317 | * @return availableBorrowsETH the borrowing power left of the user 318 | * @return currentLiquidationThreshold the liquidation threshold of the user 319 | * @return ltv the loan to value of the user 320 | * @return healthFactor the current health factor of the user 321 | **/ 322 | function getUserAccountData(address user) 323 | external 324 | view 325 | returns ( 326 | uint256 totalCollateralETH, 327 | uint256 totalDebtETH, 328 | uint256 availableBorrowsETH, 329 | uint256 currentLiquidationThreshold, 330 | uint256 ltv, 331 | uint256 healthFactor 332 | ); 333 | 334 | function initReserve( 335 | address reserve, 336 | address aTokenAddress, 337 | address stableDebtAddress, 338 | address variableDebtAddress, 339 | address interestRateStrategyAddress 340 | ) external; 341 | 342 | function setReserveInterestRateStrategyAddress(address reserve, address rateStrategyAddress) external; 343 | 344 | function setConfiguration(address reserve, uint256 configuration) external; 345 | 346 | /** 347 | * @dev Returns the configuration of the reserve 348 | * @param asset The address of the underlying asset of the reserve 349 | * @return The configuration of the reserve 350 | **/ 351 | function getConfiguration(address asset) external view returns (DataTypes.ReserveConfigurationMap memory); 352 | 353 | /** 354 | * @dev Returns the configuration of the user across all the reserves 355 | * @param user The user address 356 | * @return The configuration of the user 357 | **/ 358 | function getUserConfiguration(address user) external view returns (DataTypes.UserConfigurationMap memory); 359 | 360 | /** 361 | * @dev Returns the normalized income normalized income of the reserve 362 | * @param asset The address of the underlying asset of the reserve 363 | * @return The reserve's normalized income 364 | */ 365 | function getReserveNormalizedIncome(address asset) external view returns (uint256); 366 | 367 | /** 368 | * @dev Returns the normalized variable debt per unit of asset 369 | * @param asset The address of the underlying asset of the reserve 370 | * @return The reserve normalized variable debt 371 | */ 372 | function getReserveNormalizedVariableDebt(address asset) external view returns (uint256); 373 | 374 | /** 375 | * @dev Returns the state and configuration of the reserve 376 | * @param asset The address of the underlying asset of the reserve 377 | * @return The state of the reserve 378 | **/ 379 | function getReserveData(address asset) external view returns (DataTypes.ReserveData memory); 380 | 381 | function finalizeTransfer( 382 | address asset, 383 | address from, 384 | address to, 385 | uint256 amount, 386 | uint256 balanceFromAfter, 387 | uint256 balanceToBefore 388 | ) external; 389 | 390 | function getReservesList() external view returns (address[] memory); 391 | 392 | function getAddressesProvider() external view returns (ILendingPoolAddressesProvider); 393 | 394 | function setPause(bool val) external; 395 | 396 | function paused() external view returns (bool); 397 | } 398 | --------------------------------------------------------------------------------