part1. Uniswap V2 Start
DEFI 进阶课程
这节课将带大家了解Uniswap v2开发的基础模块,以及快速搭建项目
前言(入门)
在 DeFi (去中心化金融)赛道中,DEX(去中心化交易所) 无疑是最核心的一块,而 Uniswap 又是整个 DEX 领域中的龙头,如 SushiSwap、PancakeSwap 等都是沿袭了 Uniswap 的。因此我们用Uniswap V2来开启DeFi进阶的课程。
Uniswap v2 实现 Defi的基础模块
1.v2-Core(核心)
源码文档 Source code
核心由一个单例工厂和许多交易对组成,工厂负责创建和索引。这些工厂合约非常少,甚至是极其稀有的。这样做的简单理由是,具有较小表面积的合约更容易推理,更不容易出错,并且在功能上更优雅。也许这种设计的最大优点是系统的许多所需属性可以直接在代码中声明,几乎没有出错的余地。然而,一个缺点是核心合约在某种程度上对用户不友好。事实上,对于大多数用例,不建议直接与这些合约交互。相反,应该使用外部合约进行交互。
1.1 Factory(工厂)
0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f
参考文档 Reference documentation
工厂持有负责为币对供电的通用字节码。它的主要工作是为每个独特的代币对创建一个且只有一个智能合约。它还包含开启协议收费的逻辑。
1.2 Pairs(交易对)
参考文档 Reference documentation
货币对有两个主要目的:充当自动做市商和跟踪池代币余额。他们还公开了可用于构建去中心化价格预言机的数据。
2. v2-Periphery(外部活动)
源码 Source code
外围是一组智能合约,包括了Library(库)和Router(路由),它旨在支持与Core(核心)的特定领域交互。由于 Uniswap 的无许可性质,下面描述的合约没有特权,实际上只是可能的外围类合约宇宙的一小部分。但是,它们是如何安全有效地与 Uniswap V2 交互的有用示例。
2.1 Library(库)
参考文档 Reference documentation
库为获取数据和定价提供了各种便利功能。
2.2 Router(路由)
0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D
参考文档 Reference documentation
使用该库的路由器完全支持前端提供交易和流动性管理功能的所有基本要求。值得注意的是,它原生支持多对交易(例如 x 到 y 到 z),将 ETH 视为一等公民,并提供元交易以消除流动性。
快速开始
环境版本(本教案)
- hardhat 2.10.1
- solidity 0.8.0
- compiler 0.8.0
创建新文件夹,安全帽初始化
1 2 3 4 5 6 7 8 9
| mkdir uniswapDemo
cd uniswapDemo #安全帽初始化项目 npx hardhat #选择 Create an empty JavaScipt Project npm init -y #创建接口文件夹,以及依赖文件夹 mkdir contracts/interfaces
|
安装必要模块
1 2 3 4 5 6 7 8 9
| #项目安装安全帽 npm install --D hardhat@2.10.1 #安全帽必要模块安装 npm install --D @nomiclabs/hardhat-waffle chai @nomiclabs/hardhat-ethers ethers
#安装openzeppelin npm install @openzeppelin/contracts #uniswap v2模块(暂时不用安装) # npm i --save @uniswap/v2-core @uniswap/v2-periphery
|
安装成功后,再进行下列操作,如果安装不成功,请联系助教
修改hardhat.config.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| require("@nomiclabs/hardhat-waffle");
const MAINNET_URL = "https://eth-mainnet.g.alchemy.com/v2/WyuB7QMMto4srxT4WPnDkX-vOVYCRv3i";
module.exports = { solidity: "0.8.0", networks: { hardhat: { forking: { url: MAINNET_URL }, } } };
|
测试项目编译是否正常
1 2 3
| npx hardhat compile
#nothing to compile
|
⭐必要文件(一定要保存)
这两份文件在之后的每一次样例中都会使用!
config.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| const DAI = "0x6B175474E89094C44Da98b954EedeAC495271d0F" const USDC = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" const USDT = "0xdAC17F958D2ee523a2206206994597C13D831ec7" const WETH = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2" const WBTC = "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599"
const WETH_10 = "0xf4BB2e28688e89fCcE3c0580D37d36A7672E8A9F"
const WETH_WHALE= "0xee2826453A4Fd5AfeB7ceffeEF3fFA2320081268" const DAI_WHALE="0xF977814e90dA44bFA03b6295A0616a897441aceC" const USDC_WHALE="0x3f5CE5FBFe3E9af3971dD833D26bA9b5C936f0bE" const USDT_WHALE="0x3f5CE5FBFe3E9af3971dD833D26bA9b5C936f0bE" const WBTC_WHALE="0xF977814e90dA44bFA03b6295A0616a897441aceC"
const CDAI = "0x5d3a536E4D6DbD6114cc1Ead35777bAB948E3643" const CUSDC = "0x39AA39c021dfbaE8faC545936693aC917d5E7563" const CWBTC = "0xccF4429DB6322D5C611ee964527D42E5d685DD6a" const CETH = "0x4Ddc2D193948926D02f9B1fE9e1daa0718270ED5"
module.exports = { DAI, USDC, USDT, WETH, WBTC, WETH_10, DAI_WHALE, USDC_WHALE, USDT_WHALE, WETH_WHALE, WBTC_WHALE, CDAI, CUSDC, CWBTC, CETH, }
|
util.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| const BN = require("bn.js");
const ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
function cast(x) { if (x instanceof BN) { return x; } return new BN(x); }
function eq(x, y) { x = cast(x); y = cast(y); return x.eq(y); }
function pow(x, y) { x = cast(x); y = cast(y); return x.pow(y); }
function frac(x, n, d) { x = cast(x); n = cast(n); d = cast(d); return x.mul(n).div(d); }
module.exports = { ZERO_ADDRESS, eq, pow, frac, };
|
这两个文件都要放在test
文件夹里面