22pragma solidity ^ 0.8.30 ;
33
44import "@openzeppelin/contracts/token/ERC20/IERC20.sol " ;
5+ import "@openzeppelin/contracts/access/AccessControl.sol " ;
56import "./ISwap.sol " ;
67import "./FeeManager.sol " ;
78import "./EIP712Swap.sol " ;
9+ import "./Roles.sol " ;
10+
11+ contract LiquidityPool is ISwap , AccessControl {
12+ using Roles for bytes32 ;
813
9- contract LiquidityPool is ISwap , FeeManager {
1014 address public token0;
1115 uint256 public token0Decimals;
1216 address public token1;
@@ -16,24 +20,22 @@ contract LiquidityPool is ISwap, FeeManager {
1620 FeeManager public feeManager;
1721 EIP712Swap public eip712Swap;
1822
19- // @notice Emitted when liquidity is added to the pool
20- /// @param _token The token that was added
21- /// @param _amount The amount of tokens that were added
22- event LiquidityAdded (address indexed _token , uint256 _amount );
23+ modifier onlyAdminOrEIP712Swap () {
24+ require (
25+ hasRole (Roles.ADMIN_ROLE, msg .sender ) || hasRole (Roles.ALLOWED_EIP712_SWAP_ROLE, msg .sender ),
26+ "Not authorized for swap operations "
27+ );
28+ _;
29+ }
2330
24- // @notice Emitted when a swap is executed
25- /// @param _tokenIn The token that was swapped in
26- /// @param _tokenOut The token that was swapped out
27- /// @param _amountIn The amount of tokens that were swapped in
28- /// @param _amountOut The amount of tokens that were swapped out
31+ event LiquidityAdded (address indexed _token , uint256 _amount );
2932 event Swap (address indexed _tokenIn , address indexed _tokenOut , uint256 _amountIn , uint256 _amountOut );
3033
3134 error InsufficientTokenBalance ();
3235 error InvalidTokenAddress (address _token );
3336 error InvalidTokenPair (address _tokenIn , address _tokenOut );
3437 error InsufficientLiquidity ();
3538 error InsufficientOutputAmount (uint256 expected , uint256 actual );
36- error ExcessiveInputAmount (uint256 expected , uint256 actual );
3739 error InsufficientAllowance ();
3840
3941 constructor (
@@ -50,61 +52,88 @@ contract LiquidityPool is ISwap, FeeManager {
5052 token1Decimals = _token1Decimals;
5153 feeManager = FeeManager (_feeManager);
5254 eip712Swap = EIP712Swap (_eip712Swap);
55+
56+ // Setup roles
57+ _grantRole (DEFAULT_ADMIN_ROLE, msg .sender );
58+ _grantRole (Roles.ADMIN_ROLE, msg .sender );
59+
60+ // Grant EIP712Swap contract permission to execute swaps
61+ _grantRole (Roles.ALLOWED_EIP712_SWAP_ROLE, _eip712Swap);
62+
63+ // Set role admin relationships
64+ _setRoleAdmin (Roles.ADMIN_ROLE, DEFAULT_ADMIN_ROLE);
65+ _setRoleAdmin (Roles.ALLOWED_EIP712_SWAP_ROLE, DEFAULT_ADMIN_ROLE);
5366 }
5467
55- // @notice Add liquidity to the pool
56- /// @param _token The token to add liquidity for
57- /// @param _amount The amount of tokens to add
58- function addLiquidity (address _token , uint256 _amount ) external onlyRole (DEFAULT_ADMIN_ROLE) {
59- if (_token == token0 || _token == token1) {
68+ /// @notice Add liquidity to the pool (admin only)
69+ function addLiquidity (address _token , uint256 _amount ) external onlyRole (Roles.ADMIN_ROLE) {
70+ if (_token != token0 && _token != token1) {
6071 revert InvalidTokenAddress (_token);
6172 }
6273
63- if (IERC20 (_token).balanceOf (address ( msg .sender ) ) < _amount) {
74+ if (IERC20 (_token).balanceOf (msg .sender ) < _amount) {
6475 revert InsufficientTokenBalance ();
6576 }
6677
6778 require (IERC20 (_token).transferFrom (msg .sender , address (this ), _amount));
6879
6980 if (_token == token0) {
7081 reserveToken0 += _amount;
71- } else if (_token == token1) {
82+ } else {
7283 reserveToken1 += _amount;
7384 }
7485
7586 emit LiquidityAdded (_token, _amount);
7687 }
7788
78- // @notice Get the reserves of the pool
79- /// @return _reserveToken0 The reserve of token0
80- /// @return _reserveToken1 The reserve of token1
89+ /// @notice Remove liquidity from the pool (admin only)
90+ function removeLiquidity (address _token , uint256 _amount ) external onlyRole (Roles.ADMIN_ROLE) {
91+ if (_token != token0 && _token != token1) {
92+ revert InvalidTokenAddress (_token);
93+ }
94+
95+ uint256 currentReserve = _token == token0 ? reserveToken0 : reserveToken1;
96+ require (_amount <= currentReserve, "Insufficient reserves " );
97+
98+ require (IERC20 (_token).transfer (msg .sender , _amount));
99+
100+ if (_token == token0) {
101+ reserveToken0 -= _amount;
102+ } else {
103+ reserveToken1 -= _amount;
104+ }
105+ }
106+
107+ /// @notice Grant EIP712 swap permission to an address
108+ function grantSwapRole (address _swapper ) external onlyRole (DEFAULT_ADMIN_ROLE) {
109+ _grantRole (Roles.ALLOWED_EIP712_SWAP_ROLE, _swapper);
110+ }
111+
112+ /// @notice Revoke EIP712 swap permission from an address
113+ function revokeSwapRole (address _swapper ) external onlyRole (DEFAULT_ADMIN_ROLE) {
114+ _revokeRole (Roles.ALLOWED_EIP712_SWAP_ROLE, _swapper);
115+ }
116+
81117 function getReserves () external view returns (uint256 _reserveToken0 , uint256 _reserveToken1 ) {
82118 _reserveToken0 = reserveToken0;
83119 _reserveToken1 = reserveToken1;
84120 }
85121
86- /// @notice Get the price of the token in the pool
87- /// @param _tokenIn The token to get the price of
88- /// @param _tokenOut The token to get the price of
89- /// @return _price The price of the token in the pool
90122 function getPrice (address _tokenIn , address _tokenOut ) external view returns (uint256 _price ) {
91123 uint256 _reserveTokenIn = _tokenIn == token0 ? reserveToken0 : reserveToken1;
92124 uint256 _reserveTokenOut = _tokenOut == token0 ? reserveToken0 : reserveToken1;
93- uint256 _tokenInDecimals = _tokenIn == token0 ? token0Decimals : token1Decimals;
94- uint256 _tokenOutDecimals = _tokenOut == token0 ? token0Decimals : token1Decimals;
95125
96- _price = (_reserveTokenIn * 10 ** _tokenInDecimals) / (_reserveTokenOut * 10 ** _tokenOutDecimals);
126+ if (_reserveTokenIn == 0 || _reserveTokenOut == 0 ) {
127+ return 0 ;
128+ }
129+
130+ _price = (_reserveTokenOut * 1e18 ) / _reserveTokenIn;
97131 }
98132
99- /// @notice Swap tokens in the pool
100- /// @param _sender The address of the sender
101- /// @param _tokenIn The token to swap in
102- /// @param _tokenOut The token to swap out
103- /// @param _amountIn The amount of tokens to swap in
104- /// @param _minAmountOut The minimum amount of tokens to swap out
133+ /// @notice Execute a swap (admin or authorized EIP712 contract only)
105134 function swap (address _sender , address _tokenIn , address _tokenOut , uint256 _amountIn , uint256 _minAmountOut )
106135 external
107- onlyRole (DEFAULT_ADMIN_ROLE)
136+ onlyAdminOrEIP712Swap
108137 {
109138 if (
110139 _tokenIn != token0 && _tokenIn != token1 || _tokenOut != token0 && _tokenOut != token1
@@ -113,25 +142,35 @@ contract LiquidityPool is ISwap, FeeManager {
113142 revert InvalidTokenPair (_tokenIn, _tokenOut);
114143 }
115144
116- address _msgSender = msg . sender == address (eip712Swap) ? _sender : msg . sender ;
145+ address tokenHolder = _sender;
117146
118- if (IERC20 (_tokenIn).allowance (_msgSender , address (this )) < _amountIn) revert InsufficientAllowance ();
147+ if (IERC20 (_tokenIn).allowance (tokenHolder , address (this )) < _amountIn) revert InsufficientAllowance ();
119148
120149 uint256 _reserveTokenIn = _tokenIn == token0 ? reserveToken0 : reserveToken1;
121150 uint256 _reserveTokenOut = _tokenOut == token0 ? reserveToken0 : reserveToken1;
122- uint256 _tokenInDecimals = _tokenIn == token0 ? token0Decimals : token1Decimals;
123- uint256 _tokenOutDecimals = _tokenOut == token0 ? token0Decimals : token1Decimals;
124151
125- uint256 amountOut = (_amountIn * (10 ** _tokenOutDecimals) * _reserveTokenOut)
126- / (_reserveTokenIn + _amountIn * (10 ** _tokenOutDecimals));
127- uint256 _fee = feeManager.getFee (SwapParams (_tokenIn, _tokenOut, _amountIn, _reserveTokenIn, _reserveTokenOut));
128- amountOut -= (_fee * (10 ** _tokenOutDecimals)) / (10 ** _tokenInDecimals);
152+ if (_reserveTokenIn == 0 || _reserveTokenOut == 0 ) revert InsufficientLiquidity ();
153+ if (_amountIn >= _reserveTokenIn) revert InsufficientLiquidity ();
154+
155+ // AMM calculation
156+ uint256 amountOut = (_amountIn * _reserveTokenOut) / (_reserveTokenIn + _amountIn);
157+
158+ // Apply fee using FeeManager
159+ ISwap.SwapParams memory swapParams = ISwap.SwapParams ({
160+ token0: _tokenIn,
161+ token1: _tokenOut,
162+ amount0: _amountIn,
163+ reserveToken0: _reserveTokenIn,
164+ reserveToken1: _reserveTokenOut
165+ });
166+
167+ uint256 feeAmount = feeManager.getFee (swapParams);
168+ amountOut = amountOut > feeAmount ? amountOut - feeAmount : 0 ;
129169
130170 if (amountOut < _minAmountOut) revert InsufficientOutputAmount (_minAmountOut, amountOut);
131- if (amountOut > _reserveTokenOut) revert InsufficientLiquidity ();
132171
133- require (IERC20 (_tokenIn).transferFrom (_msgSender , address (this ), _amountIn));
134- require (IERC20 (_tokenOut).transfer (_msgSender , amountOut));
172+ require (IERC20 (_tokenIn).transferFrom (tokenHolder , address (this ), _amountIn));
173+ require (IERC20 (_tokenOut).transfer (tokenHolder , amountOut));
135174
136175 if (_tokenIn == token0) {
137176 reserveToken0 += _amountIn;
0 commit comments