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
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
| // SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
contract UniswapV2Pair is ReentrancyGuard {
string public constant name = "Uniswap V2 Pair";
string public constant symbol = "UNI-V2";
uint256 public constant MINIMUM_LIQUIDITY = 1000;
address public token0;
address public token1;
uint256 public reserve0;
uint256 public reserve1;
uint256 public totalSupply;
mapping(address => uint256) public balanceOf;
event Mint(address indexed sender, uint256 amount0, uint256 amount1);
event Burn(address indexed sender, uint256 amount0, uint256 amount1);
event Swap(
address indexed sender,
uint256 amount0In,
uint256 amount1In,
uint256 amount0Out,
uint256 amount1Out,
address indexed to
);
event Sync(uint256 reserve0, uint256 reserve1);
constructor() {
factory = msg.sender;
}
function initialize(address _token0, address _token1) external {
require(msg.sender == factory, "Forbidden");
require(_token0 < _token1, "Invalid tokens");
token0 = _token0;
token1 = _token1;
}
// 添加流动性
function mint(address to) external nonReentrant returns (uint256 liquidity) {
(uint256 reserve0_, uint256 reserve1_) = getReserves();
uint256 balance0 = IERC20(token0).balanceOf(address(this));
uint256 balance1 = IERC20(token1).balanceOf(address(this));
uint256 amount0 = balance0 - reserve0_;
uint256 amount1 = balance1 - reserve1_;
uint256 _totalSupply = totalSupply;
if (_totalSupply == 0) {
// 首次添加流动性
liquidity = Math.sqrt(amount0 * amount1) - MINIMUM_LIQUIDITY;
_mint(address(0), MINIMUM_LIQUIDITY); // 永久锁定最小流动性
} else {
liquidity = Math.min(
(amount0 * _totalSupply) / reserve0_,
(amount1 * _totalSupply) / reserve1_
);
}
require(liquidity > 0, "Insufficient liquidity minted");
_mint(to, liquidity);
_update(balance0, balance1);
emit Mint(to, amount0, amount1);
}
// 移除流动性
function burn(address to) external nonReentrant returns (uint256 amount0, uint256 amount1) {
uint256 liquidity = balanceOf[address(this)];
require(liquidity > 0, "No liquidity");
(uint256 reserve0_, uint256 reserve1_) = getReserves();
uint256 _totalSupply = totalSupply;
amount0 = (liquidity * reserve0_) / _totalSupply;
amount1 = (liquidity * reserve1_) / _totalSupply;
_burn(address(this), liquidity);
_transfer(
token0,
address(this),
to,
amount0
);
_transfer(
token1,
address(this),
to,
amount1
);
(uint256 balance0, uint256 balance1) = getBalances();
_update(balance0, balance1);
emit Burn(to, amount0, amount1);
}
// 交换(核心功能)
function swap(
uint256 amount0Out,
uint256 amount1Out,
address to,
bytes calldata data
) external nonReentrant {
require(
amount0Out > 0 || amount1Out > 0,
"Insufficient output amount"
);
(uint256 reserve0_, uint256 reserve1_) = getReserves();
if (amount0Out > 0) {
uint256 amount0In = getInputAmount(
amount0Out,
reserve0_,
reserve1_
);
require(
amount0In <= reserve0_ - amount0Out,
"Insufficient liquidity"
);
uint256 balance0Before = IERC20(token0).balanceOf(address(this));
_transfer(token0, msg.sender, address(this), amount0In);
uint256 balance0After = IERC20(token0).balanceOf(address(this));
amount0In = balance0After - balance0Before;
_transfer(token1, address(this), to, amount0Out);
}
if (amount1Out > 0) {
uint256 amount1In = getInputAmount(
amount1Out,
reserve1_,
reserve0_
);
require(
amount1In <= reserve1_ - amount1Out,
"Insufficient liquidity"
);
uint256 balance1Before = IERC20(token1).balanceOf(address(this));
_transfer(token1, msg.sender, address(this), amount1In);
uint256 balance1After = IERC20(token1).balanceOf(address(this));
amount1In = balance1After - balance1Before;
_transfer(token0, address(this), to, amount1Out);
}
(uint256 balance0, uint256 balance1) = getBalances();
_update(balance0, balance1);
emit Swap(
msg.sender,
amount0In,
amount1In,
amount0Out,
amount1Out,
to
);
}
// 计算输入量(恒定乘积公式)
function getInputAmount(
uint256 outputAmount,
uint256 inputReserve,
uint256 outputReserve
) public pure returns (uint256 inputAmount) {
require(inputReserve > 0 && outputReserve > 0, "Invalid reserves");
require(outputAmount < outputReserve, "Output amount too high");
uint256 numerator = inputReserve * outputAmount * 1000;
uint256 denominator = (outputReserve - outputAmount) * 997;
return (numerator / denominator) + 1;
}
// 滑点计算
function getAmountOut(
uint256 amountIn,
uint256 reserveIn,
uint256 reserveOut
) public pure returns (uint256 amountOut) {
require(amountIn > 0, "Insufficient input amount");
require(reserveIn > 0 && reserveOut > 0, "Invalid reserves");
uint256 amountInWithFee = amountIn * 997;
uint256 numerator = amountInWithFee * reserveOut;
uint256 denominator = reserveIn * 1000 + amountInWithFee;
return numerator / denominator;
}
function getReserves() public view returns (uint256, uint256) {
return (reserve0, reserve1);
}
function _update(uint256 balance0, uint256 balance1) private {
reserve0 = balance0;
reserve1 = balance1;
emit Sync(balance0, balance1);
}
function _mint(address to, uint256 amount) private {
totalSupply += amount;
balanceOf[to] += amount;
}
function _burn(address from, uint256 amount) private {
require(balanceOf[from] >= amount, "Insufficient balance");
balanceOf[from] -= amount;
totalSupply -= amount;
}
function _transfer(
address token,
address from,
address to,
uint256 amount
) private {
IERC20(token).transferFrom(from, to, amount);
}
function getBalances() public view returns (uint256, uint256) {
return (
IERC20(token0).balanceOf(address(this)),
IERC20(token1).balanceOf(address(this))
);
}
}
|