Allureluoli 二月FEBRUARY

区块链国赛笔记 18|Uniswap V2 Pair:流动性与交换测试阅读笔记

模拟任务要求创建交易对、添加流动性和移除流动性。原笔记主要完成源码阅读与机制分析,下面按这一范围整理。

本文由个人备赛旧笔记整理,整理日期为 2026-11-03。命令、输出和测试记录属于当时的实验环境;本文未重新搭建整套环境验证。

阅读前先确认

本文保留 Uniswap v2-core 代码学习记录,复用前请查看上游许可。源码分析不等于已完成可执行测试;mint、burn 与 swap 仍需准备代币、工厂和断言。

第二部分 2题

第2题 按要求完成智能合约测试

【要求】 分析uniswapv2合约代码,完成任务中指定的测试要求,并且将任务中编写代码、运行截图、部署至区块链截图保存至指定位置。

【任务】

1.针对UniswapV2Pair.sol 合约代码内容完成交易对创建的核心功能测试

针对Uniswap v2的合约代码完成添加流动性、移除流动性测试

没代码怎么办呢,我在github找到了

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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
pragma solidity =0.5.16;

import './interfaces/IUniswapV2Pair.sol';
import './UniswapV2ERC20.sol';
import './libraries/Math.sol';
import './libraries/UQ112x112.sol';
import './interfaces/IERC20.sol';
import './interfaces/IUniswapV2Factory.sol';
import './interfaces/IUniswapV2Callee.sol';

contract UniswapV2Pair is IUniswapV2Pair, UniswapV2ERC20 {
using SafeMath for uint;
using UQ112x112 for uint224;

uint public constant MINIMUM_LIQUIDITY = 10**3;
bytes4 private constant SELECTOR = bytes4(keccak256(bytes('transfer(address,uint256)')));

address public factory;
address public token0;
address public token1;

uint112 private reserve0; // uses single storage slot, accessible via getReserves
uint112 private reserve1; // uses single storage slot, accessible via getReserves
uint32 private blockTimestampLast; // uses single storage slot, accessible via getReserves

uint public price0CumulativeLast;
uint public price1CumulativeLast;
uint public kLast; // reserve0 * reserve1, as of immediately after the most recent liquidity event

uint private unlocked = 1;
modifier lock() {
require(unlocked == 1, 'UniswapV2: LOCKED');
unlocked = 0;
_;
unlocked = 1;
}

function getReserves() public view returns (uint112 _reserve0, uint112 _reserve1, uint32 _blockTimestampLast) {
_reserve0 = reserve0;
_reserve1 = reserve1;
_blockTimestampLast = blockTimestampLast;
}

function _safeTransfer(address token, address to, uint value) private {
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(SELECTOR, to, value));
require(success && (data.length == 0 || abi.decode(data, (bool))), 'UniswapV2: TRANSFER_FAILED');
}

event Mint(address indexed sender, uint amount0, uint amount1);
event Burn(address indexed sender, uint amount0, uint amount1, address indexed to);
event Swap(
address indexed sender,
uint amount0In,
uint amount1In,
uint amount0Out,
uint amount1Out,
address indexed to
);
event Sync(uint112 reserve0, uint112 reserve1);

constructor() public {
factory = msg.sender;
}

// called once by the factory at time of deployment
function initialize(address _token0, address _token1) external {
require(msg.sender == factory, 'UniswapV2: FORBIDDEN'); // sufficient check
token0 = _token0;
token1 = _token1;
}

// update reserves and, on the first call per block, price accumulators
function _update(uint balance0, uint balance1, uint112 _reserve0, uint112 _reserve1) private {
require(balance0 <= uint112(-1) && balance1 <= uint112(-1), 'UniswapV2: OVERFLOW');
uint32 blockTimestamp = uint32(block.timestamp % 2**32);
uint32 timeElapsed = blockTimestamp - blockTimestampLast; // overflow is desired
if (timeElapsed > 0 && _reserve0 != 0 && _reserve1 != 0) {
// * never overflows, and + overflow is desired
price0CumulativeLast += uint(UQ112x112.encode(_reserve1).uqdiv(_reserve0)) * timeElapsed;
price1CumulativeLast += uint(UQ112x112.encode(_reserve0).uqdiv(_reserve1)) * timeElapsed;
}
reserve0 = uint112(balance0);
reserve1 = uint112(balance1);
blockTimestampLast = blockTimestamp;
emit Sync(reserve0, reserve1);
}

// if fee is on, mint liquidity equivalent to 1/6th of the growth in sqrt(k)
function _mintFee(uint112 _reserve0, uint112 _reserve1) private returns (bool feeOn) {
address feeTo = IUniswapV2Factory(factory).feeTo();
feeOn = feeTo != address(0);
uint _kLast = kLast; // gas savings
if (feeOn) {
if (_kLast != 0) {
uint rootK = Math.sqrt(uint(_reserve0).mul(_reserve1));
uint rootKLast = Math.sqrt(_kLast);
if (rootK > rootKLast) {
uint numerator = totalSupply.mul(rootK.sub(rootKLast));
uint denominator = rootK.mul(5).add(rootKLast);
uint liquidity = numerator / denominator;
if (liquidity > 0) _mint(feeTo, liquidity);
}
}
} else if (_kLast != 0) {
kLast = 0;
}
}

// this low-level function should be called from a contract which performs important safety checks
function mint(address to) external lock returns (uint liquidity) {
(uint112 _reserve0, uint112 _reserve1,) = getReserves(); // gas savings
uint balance0 = IERC20(token0).balanceOf(address(this));
uint balance1 = IERC20(token1).balanceOf(address(this));
uint amount0 = balance0.sub(_reserve0);
uint amount1 = balance1.sub(_reserve1);

bool feeOn = _mintFee(_reserve0, _reserve1);
uint _totalSupply = totalSupply; // gas savings, must be defined here since totalSupply can update in _mintFee
if (_totalSupply == 0) {
liquidity = Math.sqrt(amount0.mul(amount1)).sub(MINIMUM_LIQUIDITY);
_mint(address(0), MINIMUM_LIQUIDITY); // permanently lock the first MINIMUM_LIQUIDITY tokens
} else {
liquidity = Math.min(amount0.mul(_totalSupply) / _reserve0, amount1.mul(_totalSupply) / _reserve1);
}
require(liquidity > 0, 'UniswapV2: INSUFFICIENT_LIQUIDITY_MINTED');
_mint(to, liquidity);

_update(balance0, balance1, _reserve0, _reserve1);
if (feeOn) kLast = uint(reserve0).mul(reserve1); // reserve0 and reserve1 are up-to-date
emit Mint(msg.sender, amount0, amount1);
}

// this low-level function should be called from a contract which performs important safety checks
function burn(address to) external lock returns (uint amount0, uint amount1) {
(uint112 _reserve0, uint112 _reserve1,) = getReserves(); // gas savings
address _token0 = token0; // gas savings
address _token1 = token1; // gas savings
uint balance0 = IERC20(_token0).balanceOf(address(this));
uint balance1 = IERC20(_token1).balanceOf(address(this));
uint liquidity = balanceOf[address(this)];

bool feeOn = _mintFee(_reserve0, _reserve1);
uint _totalSupply = totalSupply; // gas savings, must be defined here since totalSupply can update in _mintFee
amount0 = liquidity.mul(balance0) / _totalSupply; // using balances ensures pro-rata distribution
amount1 = liquidity.mul(balance1) / _totalSupply; // using balances ensures pro-rata distribution
require(amount0 > 0 && amount1 > 0, 'UniswapV2: INSUFFICIENT_LIQUIDITY_BURNED');
_burn(address(this), liquidity);
_safeTransfer(_token0, to, amount0);
_safeTransfer(_token1, to, amount1);
balance0 = IERC20(_token0).balanceOf(address(this));
balance1 = IERC20(_token1).balanceOf(address(this));

_update(balance0, balance1, _reserve0, _reserve1);
if (feeOn) kLast = uint(reserve0).mul(reserve1); // reserve0 and reserve1 are up-to-date
emit Burn(msg.sender, amount0, amount1, to);
}

// this low-level function should be called from a contract which performs important safety checks
function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external lock {
require(amount0Out > 0 || amount1Out > 0, 'UniswapV2: INSUFFICIENT_OUTPUT_AMOUNT');
(uint112 _reserve0, uint112 _reserve1,) = getReserves(); // gas savings
require(amount0Out < _reserve0 && amount1Out < _reserve1, 'UniswapV2: INSUFFICIENT_LIQUIDITY');

uint balance0;
uint balance1;
{ // scope for _token{0,1}, avoids stack too deep errors
address _token0 = token0;
address _token1 = token1;
require(to != _token0 && to != _token1, 'UniswapV2: INVALID_TO');
if (amount0Out > 0) _safeTransfer(_token0, to, amount0Out); // optimistically transfer tokens
if (amount1Out > 0) _safeTransfer(_token1, to, amount1Out); // optimistically transfer tokens
if (data.length > 0) IUniswapV2Callee(to).uniswapV2Call(msg.sender, amount0Out, amount1Out, data);
balance0 = IERC20(_token0).balanceOf(address(this));
balance1 = IERC20(_token1).balanceOf(address(this));
}
uint amount0In = balance0 > _reserve0 - amount0Out ? balance0 - (_reserve0 - amount0Out) : 0;
uint amount1In = balance1 > _reserve1 - amount1Out ? balance1 - (_reserve1 - amount1Out) : 0;
require(amount0In > 0 || amount1In > 0, 'UniswapV2: INSUFFICIENT_INPUT_AMOUNT');
{ // scope for reserve{0,1}Adjusted, avoids stack too deep errors
uint balance0Adjusted = balance0.mul(1000).sub(amount0In.mul(3));
uint balance1Adjusted = balance1.mul(1000).sub(amount1In.mul(3));
require(balance0Adjusted.mul(balance1Adjusted) >= uint(_reserve0).mul(_reserve1).mul(1000**2), 'UniswapV2: K');
}

_update(balance0, balance1, _reserve0, _reserve1);
emit Swap(msg.sender, amount0In, amount1In, amount0Out, amount1Out, to);
}

// force balances to match reserves
function skim(address to) external lock {
address _token0 = token0; // gas savings
address _token1 = token1; // gas savings
_safeTransfer(_token0, to, IERC20(_token0).balanceOf(address(this)).sub(reserve0));
_safeTransfer(_token1, to, IERC20(_token1).balanceOf(address(this)).sub(reserve1));
}

// force reserves to match balances
function sync() external lock {
_update(IERC20(token0).balanceOf(address(this)), IERC20(token1).balanceOf(address(this)), reserve0, reserve1);
}
}

对代码进行分析

合约基础结构

继承关系:继承自 UniswapV2ERC20,实现了 IUniswapV2Pair 接口

核心状态变量:

  • reserve0, reserve1:两种代币的储备量
  • price0CumulativeLast, price1CumulativeLast:价格累加器,用于计算时间加权平均价格
  • kLast:恒定乘积 k = reserve0 * reserve1

关键功能解析

1. 重入锁保护

1
2
3
4
5
6
7
modifier lock() {

require(unlocked == 1, 'UniswapV2: LOCKED');
unlocked = 0;
_;
unlocked = 1;
}

防止重入攻击,确保函数执行原子性。

2. 流动性提供(mint)

1
function mint(address to) external lock returns (uint liquidity)
  • 首次添加流动性时锁定 MINIMUM_LIQUIDITY
  • 根据投入的代币比例计算流动性代币数量
  • 更新储备量和价格累加器

3. 流动性移除(burn)

1
function burn(address to) external lock returns (uint amount0, uint amount1)
  • 按比例返还两种代币
  • 销毁对应的流动性代币

4. 代币交换(swap)

1
function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data)

核心机制:

  • 支持闪电贷(通过 IUniswapV2Callee 回调)
  • 0.3% 的交易手续费
  • 恒定乘积公式验证:(balance0 * 1000 - amount0In * 3) * (balance1 * 1000 - amount1In * 3) >= reserve0 * reserve1 * 1000^2

5. 手续费机制

1
function _mintFee(uint112 _reserve0, uint112 _reserve1) private returns (bool feeOn)
  • 如果工厂合约设置了 feeTo 地址,则收取协议费用
  • 费用为流动性增长的 1/6(增长部分的 1/6)

6. 价格计算

1
price0CumulativeLast += uint(UQ112x112.encode(_reserve1).uqdiv(_reserve0)) * timeElapsed;

使用 Q112x112 定点数格式进行精确的价格计算,避免浮点数运算。

特性

  1. 重入保护:所有关键函数都使用 lock 修饰符
  2. 溢出检查:使用 SafeMath 进行数学运算
  3. 输入验证:严格验证输入参数的有效性
  4. 余额验证:交易后验证恒定乘积不变

  1. Gas 优化:通过局部变量缓存减少存储读取
  2. 堆栈深度优化:使用作用域块避免堆栈过深错误
  3. 精确计算:使用定点数运算保证价格计算的准确性
  4. 扩展性:支持闪电贷和自定义回调

这个合约实现了完整的 AMM 交易对功能,是 DeFi 领域最经典和广泛使用的合约设计之一。


测试脚本编写

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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
const { expect } = require("chai");
const { ethers } = require("hardhat");

describe("UniswapV2Pair", function () {
let factory, pair, tokenA, tokenB, deployer;

beforeEach(async function () {
try {
console.log("Starting beforeEach...");
// 获取部署者账户
[deployer] = await ethers.getSigners();
console.log("Deployer address:", deployer.address);

// 部署测试代币
console.log("Deploying ERC20 tokens...");
const Token = await ethers.getContractFactory("contracts/ERC20.sol:ERC20");
tokenA = await Token.deploy("Test Token A", "TTA", ethers.parseEther("1000000"));
tokenB = await Token.deploy("Test Token B", "TTB", ethers.parseEther("1000000"));
await tokenA.waitForDeployment();
await tokenB.waitForDeployment();
console.log("TokenA deployed to:", await tokenA.getAddress());
console.log("TokenB deployed to:", await tokenB.getAddress());

// 检查代币余额
const balanceA = await tokenA.balanceOf(deployer.address);
const balanceB = await tokenB.balanceOf(deployer.address);
console.log("TokenA balance:", ethers.formatEther(balanceA));
console.log("TokenB balance:", ethers.formatEther(balanceB));

// 部署工厂合约
console.log("Deploying UniswapV2Factory...");
const Factory = await ethers.getContractFactory("UniswapV2Factory");
factory = await Factory.deploy(deployer.address);
await factory.waitForDeployment();
console.log("UniswapV2Factory deployed to:", await factory.getAddress());

// 创建交易对
console.log("Creating pair...");
await factory.createPair(await tokenA.getAddress(), await tokenB.getAddress());
const pairAddress = await factory.getPair(await tokenA.getAddress(), await tokenB.getAddress());
console.log("UniswapV2Pair deployed to:", pairAddress);
if (!pairAddress || pairAddress === "0x0000000000000000000000000000000000000000") {
throw new Error("Pair address is invalid");
}
pair = await ethers.getContractAt("UniswapV2Pair", pairAddress);
} catch (error) {
console.error("Error in beforeEach:", error);
throw error;
}
});

it("should initialize pair correctly", async function () {
try {
console.log("Running test: should initialize pair correctly...");
expect(await pair.factory()).to.equal(await factory.getAddress());
const token0 = await pair.token0();
const token1 = await pair.token1();
console.log("Token0:", token0);
console.log("Token1:", token1);
const tokens = [(await tokenA.getAddress()), (await tokenB.getAddress())].sort();
const pairTokens = [token0, token1].sort();
expect(tokens[0]).to.equal(pairTokens[0]);
expect(tokens[1]).to.equal(pairTokens[1]);
} catch (error) {
console.error("Error in test 'initialize pair':", error);
throw error;
}
});

it("should add liquidity directly to pair", async function () {
try {
console.log("Running test: should add liquidity directly to pair...");
const amountA = ethers.parseEther("1000");
const amountB = ethers.parseEther("1000");

// 确定代币顺序
const token0 = await pair.token0();
const tokenAIsToken0 = (await tokenA.getAddress()).toLowerCase() === token0.toLowerCase();
const [amountADesired, amountBDesired] = tokenAIsToken0
? [amountA, amountB]
: [amountB, amountA];

// 授权交易对使用代币
console.log("Approving tokens...");
await tokenA.approve(await pair.getAddress(), amountA);
await tokenB.approve(await pair.getAddress(), amountB);

// 转账代币到交易对
console.log("Transferring tokens to pair...");
await tokenA.transfer(await pair.getAddress(), amountADesired);
await tokenB.transfer(await pair.getAddress(), amountBDesired);

// 调用 mint 添加流动性
console.log("Minting liquidity...");
await pair.mint(deployer.address);

// 检查 LP 代币余额
const lpBalance = await pair.balanceOf(deployer.address);
console.log("LP balance:", ethers.formatEther(lpBalance));
expect(lpBalance).to.be.gt(0);
} catch (error) {
console.error("Error in test 'add liquidity':", error);
throw error;
}
});

it("should remove liquidity directly from pair", async function () {
try {
console.log("Running test: should remove liquidity directly from pair...");
const amountA = ethers.parseEther("1000");
const amountB = ethers.parseEther("1000");

// 确定代币顺序
const token0 = await pair.token0();
const tokenAIsToken0 = (await tokenA.getAddress()).toLowerCase() === token0.toLowerCase();
const [amountADesired, amountBDesired] = tokenAIsToken0
? [amountA, amountB]
: [amountB, amountA];

// 添加流动性
console.log("Adding liquidity...");
await tokenA.approve(await pair.getAddress(), amountA);
await tokenB.approve(await pair.getAddress(), amountB);
await tokenA.transfer(await pair.getAddress(), amountADesired);
await tokenB.transfer(await pair.getAddress(), amountBDesired);
await pair.mint(deployer.address);

// 获取 LP 代币余额
const lpBalance = await pair.balanceOf(deployer.address);
console.log("LP balance:", ethers.formatEther(lpBalance));
expect(lpBalance).to.be.gt(0);

// 授权交易对使用 LP 代币
console.log("Approving LP tokens...");
await pair.approve(await pair.getAddress(), lpBalance);

// 将 LP 代币转移到交易对
console.log("Transferring LP tokens to pair...");
await pair.transfer(await pair.getAddress(), lpBalance);

// 记录移除前的代币余额
const initialBalanceA = await tokenA.balanceOf(deployer.address);
const initialBalanceB = await tokenB.balanceOf(deployer.address);
console.log("Initial TokenA balance:", ethers.formatEther(initialBalanceA));
console.log("Initial TokenB balance:", ethers.formatEther(initialBalanceB));

// 调用 burn 移除流动性
console.log("Burning liquidity...");
await pair.burn(deployer.address);

// 检查代币余额增加
const finalBalanceA = await tokenA.balanceOf(deployer.address);
const finalBalanceB = await tokenB.balanceOf(deployer.address);
console.log("Final TokenA balance:", ethers.formatEther(finalBalanceA));
console.log("Final TokenB balance:", ethers.formatEther(finalBalanceB));
expect(finalBalanceA).to.be.gt(initialBalanceA);
expect(finalBalanceB).to.be.gt(initialBalanceB);
} catch (error) {
console.error("Error in test 'remove liquidity':", error);
throw error;
}
});
});

启动本地节点

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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
PS E:\code\solidity_project\uniswap-v2-test> npx hardhat node
Started HTTP and WebSocket JSON-RPC server at http://127.0.0.1:8545/

Accounts
========

WARNING: These accounts, and their private keys, are publicly known.
Any funds sent to them on Mainnet or any other live network WILL BE LOST.

Account #0: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 (10000 ETH)
Private Key: [本地开发账户密钥已省略]

Account #1: 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 (10000 ETH)
Private Key: [本地开发账户密钥已省略]

Account #2: 0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC (10000 ETH)
Private Key: [本地开发账户密钥已省略]

Account #3: 0x90F79bf6EB2c4f870365E785982E1f101E93b906 (10000 ETH)
Private Key: [本地开发账户密钥已省略]

Account #4: 0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65 (10000 ETH)
Private Key: [本地开发账户密钥已省略]

Account #5: 0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc (10000 ETH)
Private Key: [本地开发账户密钥已省略]

Account #6: 0x976EA74026E726554dB657fA54763abd0C3a0aa9 (10000 ETH)
Private Key: [本地开发账户密钥已省略]

Account #7: 0x14dC79964da2C08b23698B3D3cc7Ca32193d9955 (10000 ETH)
Private Key: [本地开发账户密钥已省略]

Account #8: 0x23618e81E3f5cdF7f54C3d65f7FBc0aBf5B21E8f (10000 ETH)
Private Key: [本地开发账户密钥已省略]

Account #9: 0xa0Ee7A142d267C1f36714E4a8F75612F20a79720 (10000 ETH)
Private Key: [本地开发账户密钥已省略]

Account #10: 0xBcd4042DE499D14e55001CcbB24a551F3b954096 (10000 ETH)
Private Key: [本地开发账户密钥已省略]

Account #11: 0x71bE63f3384f5fb98995898A86B02Fb2426c5788 (10000 ETH)
Private Key: [本地开发账户密钥已省略]

Account #12: 0xFABB0ac9d68B0B445fB7357272Ff202C5651694a (10000 ETH)
Private Key: [本地开发账户密钥已省略]

Account #13: 0x1CBd3b2770909D4e10f157cABC84C7264073C9Ec (10000 ETH)
Private Key: [本地开发账户密钥已省略]

Account #14: 0xdF3e18d64BC6A983f673Ab319CCaE4f1a57C7097 (10000 ETH)
Private Key: [本地开发账户密钥已省略]

Account #15: 0xcd3B766CCDd6AE721141F452C550Ca635964ce71 (10000 ETH)
Private Key: [本地开发账户密钥已省略]

Account #16: 0x2546BcD3c84621e976D8185a91A922aE77ECEc30 (10000 ETH)
Private Key: [本地开发账户密钥已省略]

Account #17: 0xbDA5747bFD65F08deb54cb465eB87D40e51B197E (10000 ETH)
Private Key: [本地开发账户密钥已省略]

Account #18: 0xdD2FD4581271e230360230F9337D5c0430Bf44C0 (10000 ETH)
Private Key: [本地开发账户密钥已省略]

Account #19: 0x8626f6940E2eb28930eFb4CeF49B2d1F2C9C1199 (10000 ETH)
Private Key: [本地开发账户密钥已省略]

WARNING: These accounts, and their private keys, are publicly known.
Any funds sent to them on Mainnet or any other live network WILL BE LOST.

eth_accounts
eth_chainId
eth_accounts
eth_chainId (2)
eth_accounts
hardhat_addCompilationResult

执行测试

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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
PS E:\code\solidity_project\uniswap-v2-test> npx hardhat test --show-stack-traces


UniswapV2Pair
Starting beforeEach...
Deployer address: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
Deploying ERC20 tokens...
TokenA deployed to: 0x5FbDB2315678afecb367f032d93F642f64180aa3
TokenB deployed to: 0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512
TokenA balance: 1000000.0
TokenB balance: 1000000.0
Deploying UniswapV2Factory...
UniswapV2Factory deployed to: 0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0
Creating pair...
UniswapV2Pair deployed to: 0x40090456C94D75cd9864cD6FaB7272eC0B580d0E
Running test: should initialize pair correctly...
Token0: 0x5FbDB2315678afecb367f032d93F642f64180aa3
Token1: 0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512
✔ should initialize pair correctly
Starting beforeEach...
Deployer address: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
Deploying ERC20 tokens...
TokenA deployed to: 0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9
TokenB deployed to: 0x5FC8d32690cc91D4c39d9d3abcBD16989F875707
TokenA balance: 1000000.0
TokenB balance: 1000000.0
Deploying UniswapV2Factory...
UniswapV2Factory deployed to: 0x0165878A594ca255338adfa4d48449f69242Eb8F
Creating pair...
UniswapV2Pair deployed to: 0xda783516eb63CAc455AEC0f0f127e98B9DF1b431
Running test: should add liquidity directly to pair...
Approving tokens...
Transferring tokens to pair...
Minting liquidity...
LP balance: 999.999999999999999
✔ should add liquidity directly to pair
Starting beforeEach...
Deployer address: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
Deploying ERC20 tokens...
TokenA deployed to: 0x0DCd1Bf9A1b36cE34237eEaFef220932846BCD82
TokenB deployed to: 0x9A676e781A523b5d0C0e43731313A708CB607508
TokenA balance: 1000000.0
TokenB balance: 1000000.0
Deploying UniswapV2Factory...
UniswapV2Factory deployed to: 0x0B306BF915C4d645ff596e518fAf3F9669b97016
Creating pair...
UniswapV2Pair deployed to: 0xbe3bFc081A2B87263228795Cd21c94A98551d7C2
Running test: should remove liquidity directly from pair...
Adding liquidity...
LP balance: 999.999999999999999
Approving LP tokens...
Transferring LP tokens to pair...
Initial TokenA balance: 999000.0
Initial TokenB balance: 999000.0
Burning liquidity...
Final TokenA balance: 999999.999999999999999
Final TokenB balance: 999999.999999999999999
✔ should remove liquidity directly from pair


3 passing (241ms)

·············································································································
| Solidity and Network Configuration │
························|·················|···············|·················|································
| Solidity: 0.5.16 · Optim: true · Runs: 200 · viaIR: false · Block: 30,000,000 gas │
························|·················|···············|·················|································
| Methods │
························|·················|···············|·················|················|···············
| Contracts / Methods · Min · Max · Avg · # calls · usd (avg) │
························|·················|···············|·················|················|···············
| ERC20 · │
························|·················|···············|·················|················|···············
| approve · 46,044 · 46,178 · 46,071 · 5 · - │
························|·················|···············|·················|················|···············
| transfer · 46,756 · 51,296 · 50,388 · 5 · - │
························|·················|···············|·················|················|···············
| UniswapV2Factory · │
························|·················|···············|·················|················|···············
| createPair · 2,030,613 · 2,030,615 · 2,030,614 · 3 · - │
························|·················|···············|·················|················|···············
| UniswapV2Pair · │
························|·················|···············|·················|················|···············
| burn · - · - · 135,758 · 1 · - │
························|·················|···············|·················|················|···············
| mint · - · - · 154,452 · 2 · - │
························|·················|···············|·················|················|···············
| Deployments · · % of limit · │
························|·················|···············|·················|················|···············
| ERC20 · - · - · 499,099 · 1.7 % · - │
························|·················|···············|·················|················|···············
| UniswapV2Factory · - · - · 2,414,728 · 8 % · - │
························|·················|···············|·················|················|···············
| Key │
·············································································································
| ◯ Execution gas for this method does not include intrinsic gas overhead │
·············································································································
| △ Cost was non-zero but below the precision setting for the currency display (see options) │
·············································································································
| Toolchain: hardhat │
·············································································································

复现时的自查

  • 记录自己实际使用的版本、配置和缺失依赖。
  • 对照本篇关键步骤,保存命令或调用结果,并解释结果代表什么。
  • 对失败或与原记录不一致的情况保留错误信息,不把历史输出当成自己的验证结果。

返回系列导读 · 上一篇 · 下一篇

评论COMMENTS