-
Notifications
You must be signed in to change notification settings - Fork 8
Expand file tree
/
Copy pathLiquidityPool.sol
More file actions
185 lines (148 loc) · 6.44 KB
/
LiquidityPool.sol
File metadata and controls
185 lines (148 loc) · 6.44 KB
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
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/access/AccessControl.sol";
import "./ISwap.sol";
import "./FeeManager.sol";
import "./EIP712Swap.sol";
import "./Roles.sol";
contract LiquidityPool is ISwap, AccessControl {
using Roles for bytes32;
address public token0;
uint256 public token0Decimals;
address public token1;
uint256 public token1Decimals;
uint256 public reserveToken0;
uint256 public reserveToken1;
FeeManager public feeManager;
EIP712Swap public eip712Swap;
modifier onlyAdminOrEIP712Swap() {
require(
hasRole(Roles.ADMIN_ROLE, msg.sender) || hasRole(Roles.ALLOWED_EIP712_SWAP_ROLE, msg.sender),
"Not authorized for swap operations"
);
_;
}
event LiquidityAdded(address indexed _token, uint256 _amount);
event Swap(address indexed _tokenIn, address indexed _tokenOut, uint256 _amountIn, uint256 _amountOut);
error InsufficientTokenBalance();
error InvalidTokenAddress(address _token);
error InvalidTokenPair(address _tokenIn, address _tokenOut);
error InsufficientLiquidity();
error InsufficientOutputAmount(uint256 expected, uint256 actual);
error InsufficientAllowance();
constructor(
address _token0,
uint256 _token0Decimals,
address _token1,
uint256 _token1Decimals,
address _feeManager,
address _eip712Swap
) {
token0 = _token0;
token0Decimals = _token0Decimals;
token1 = _token1;
token1Decimals = _token1Decimals;
feeManager = FeeManager(_feeManager);
eip712Swap = EIP712Swap(_eip712Swap);
// Setup roles
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
_grantRole(Roles.ADMIN_ROLE, msg.sender);
// Grant EIP712Swap contract permission to execute swaps
_grantRole(Roles.ALLOWED_EIP712_SWAP_ROLE, _eip712Swap);
// Set role admin relationships
_setRoleAdmin(Roles.ADMIN_ROLE, DEFAULT_ADMIN_ROLE);
_setRoleAdmin(Roles.ALLOWED_EIP712_SWAP_ROLE, DEFAULT_ADMIN_ROLE);
}
/// @notice Add liquidity to the pool (admin only)
function addLiquidity(address _token, uint256 _amount) external onlyRole(Roles.ADMIN_ROLE) {
if (_token != token0 && _token != token1) {
revert InvalidTokenAddress(_token);
}
if (IERC20(_token).balanceOf(msg.sender) < _amount) {
revert InsufficientTokenBalance();
}
require(IERC20(_token).transferFrom(msg.sender, address(this), _amount));
if (_token == token0) {
reserveToken0 += _amount;
} else {
reserveToken1 += _amount;
}
emit LiquidityAdded(_token, _amount);
}
/// @notice Remove liquidity from the pool (admin only)
function removeLiquidity(address _token, uint256 _amount) external onlyRole(Roles.ADMIN_ROLE) {
if (_token != token0 && _token != token1) {
revert InvalidTokenAddress(_token);
}
uint256 currentReserve = _token == token0 ? reserveToken0 : reserveToken1;
require(_amount <= currentReserve, "Insufficient reserves");
require(IERC20(_token).transfer(msg.sender, _amount));
if (_token == token0) {
reserveToken0 -= _amount;
} else {
reserveToken1 -= _amount;
}
}
/// @notice Grant EIP712 swap permission to an address
function grantSwapRole(address _swapper) external onlyRole(DEFAULT_ADMIN_ROLE) {
_grantRole(Roles.ALLOWED_EIP712_SWAP_ROLE, _swapper);
}
/// @notice Revoke EIP712 swap permission from an address
function revokeSwapRole(address _swapper) external onlyRole(DEFAULT_ADMIN_ROLE) {
_revokeRole(Roles.ALLOWED_EIP712_SWAP_ROLE, _swapper);
}
function getReserves() external view returns (uint256 _reserveToken0, uint256 _reserveToken1) {
_reserveToken0 = reserveToken0;
_reserveToken1 = reserveToken1;
}
function getPrice(address _tokenIn, address _tokenOut) external view returns (uint256 _price) {
uint256 _reserveTokenIn = _tokenIn == token0 ? reserveToken0 : reserveToken1;
uint256 _reserveTokenOut = _tokenOut == token0 ? reserveToken0 : reserveToken1;
if (_reserveTokenIn == 0 || _reserveTokenOut == 0) {
return 0;
}
_price = (_reserveTokenOut * 1e18) / _reserveTokenIn;
}
/// @notice Execute a swap (admin or authorized EIP712 contract only)
function swap(address _sender, address _tokenIn, address _tokenOut, uint256 _amountIn, uint256 _minAmountOut)
external
onlyAdminOrEIP712Swap
{
if (
_tokenIn != token0 && _tokenIn != token1 || _tokenOut != token0 && _tokenOut != token1
|| _tokenIn == _tokenOut
) {
revert InvalidTokenPair(_tokenIn, _tokenOut);
}
address tokenHolder = _sender;
if (IERC20(_tokenIn).allowance(tokenHolder, address(this)) < _amountIn) revert InsufficientAllowance();
uint256 _reserveTokenIn = _tokenIn == token0 ? reserveToken0 : reserveToken1;
uint256 _reserveTokenOut = _tokenOut == token0 ? reserveToken0 : reserveToken1;
if (_reserveTokenIn == 0 || _reserveTokenOut == 0) revert InsufficientLiquidity();
if (_amountIn >= _reserveTokenIn) revert InsufficientLiquidity();
// AMM calculation
uint256 amountOut = (_amountIn * _reserveTokenOut) / (_reserveTokenIn + _amountIn);
// Apply fee using FeeManager
ISwap.SwapParams memory swapParams = ISwap.SwapParams({
token0: _tokenIn,
token1: _tokenOut,
amount0: _amountIn,
reserveToken0: _reserveTokenIn,
reserveToken1: _reserveTokenOut
});
uint256 feeAmount = feeManager.getFee(swapParams);
amountOut = amountOut > feeAmount ? amountOut - feeAmount : 0;
if (amountOut < _minAmountOut) revert InsufficientOutputAmount(_minAmountOut, amountOut);
require(IERC20(_tokenIn).transferFrom(tokenHolder, address(this), _amountIn));
require(IERC20(_tokenOut).transfer(tokenHolder, amountOut));
if (_tokenIn == token0) {
reserveToken0 += _amountIn;
reserveToken1 -= amountOut;
} else {
reserveToken1 += _amountIn;
reserveToken0 -= amountOut;
}
emit Swap(_tokenIn, _tokenOut, _amountIn, amountOut);
}
}