pragma solidity ^0.8.0; import "@openzeppelin/contracts/token/ERC20/IERC20.ERC20.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; contract LiquidityPool is Ownable { // Token addresses address public tokenA; address public tokenB; // Reserves uint256 public reserveA; uint256 public reserveB; // Constant product formula (k) uint256 public k; // Events event Swap(address indexed user, address indexed tokenIn, uint256 amountIn, address indexed tokenOut, uint256 amountOut); event AddLiquidity(address indexed user, uint256 amountA, uint256 amountB); event RemoveLiquidity(address indexed user, uint256 amountA, uint256 amountB); constructor(address _tokenA, address _tokenB) { require(_tokenA != address(0) && _tokenB != address(0), "Invalid token addresses"); tokenA = _tokenA; tokenB = _tokenB; } function swap(address _tokenIn, uint256 _amountIn) external { require(_tokenIn == tokenA || _tokenIn == tokenB, "Invalid token"); require(_amountIn > 0, "Amount must be greater than 0"); bool isTokenA = _tokenIn == tokenA; address tokenOut = isTokenA ? tokenB : tokenA; uint256 reserveIn = isTokenA ? reserveA : reserveB; uint256 reserveOut = isTokenA ? reserveB : reserveA; // Transfer tokens in IERC20(_tokenIn).transferFrom(msg.sender, address(this), _amountIn); // Calculate output amount (0.3% fee) uint256 amountInWithFee = _amountIn * 997; uint256 numerator = amountInWithFee * reserveOut; uint256 denominator = (reserveIn * 1000) + amountInWithFee; uint256 amountOut = numerator / denominator; // Update reserves if (isTokenA) { reserveA += _amountIn; reserveB -= amountOut; } else { reserveB += _amountIn; reserveA -= amountOut; } // Maintain k (optional, for stricter adherence to constant product) _updateK(); // Transfer tokens out IERC20(tokenOut).transfer(msg.sender, amountOut); emit Swap(msg.sender, _tokenIn, _amountIn, tokenOut, amountOut); } function addLiquidity(uint256 _amountA, uint256 _amountB) external { require(_amountA > 0 && _amountB > 0, "Amounts must be greater than 0"); //Transfer tokens to the contract IERC20(tokenA).transferFrom(msg.sender, address(this), _amountA); IERC20(tokenB).transferFrom(msg.sender, address(this), _amountB); // Update reserves reserveA += _amountA; reserveB += _amountB; _updateK(); // Important to call after reserves are updated. emit AddLiquidity(msg.sender, _amountA, _amountB); } //Simplified remove liquidity (proportional to LP tokens not implemented here) function removeLiquidity(uint256 _amountA, uint256 _amountB) external onlyOwner { //Simplified - owner only for demo require(_amountA > 0 && _amountB > 0, "Amounts must be greater than zero"); require(_amountA <= reserveA && _amountB <= reserveB, "Insufficient liquidity"); reserveA -= _amountA; reserveB -= _amountB; _updateK(); // Important to call after reserves are updated. IERC20(tokenA).transfer(msg.sender, _amountA); IERC20(tokenB).transfer(msg.sender, _amountB); emit RemoveLiquidity(msg.sender, _amountA, _amountB); } function _updateK() private { k = reserveA * reserveB; // Recalculates k } function getReserves() external view returns (uint256, uint256) { return (reserveA, reserveB); } }
0.4.18