# Workshop 1
```// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.10;
import "@openzeppelin/token/ERC20/IERC20.sol";
contract DEX {
address public owner;
bool public paused;
IERC20 public token1;
IERC20 public token2;
uint256 private k;
uint256 public totalShares;
mapping (address => uint256) public shares;
constructor(IERC20 _token1, IERC20 _token2, address _owner) {
token1 = _token1;
token2 = _token2;
owner = _owner;
}
function init(uint256 amount1, uint256 amount2) external {
require(token1.balanceOf(address(this)) == 0 && token2.balanceOf(address(this)) == 0);
require(token1.transferFrom(msg.sender, address(this), amount1));
require(token2.transferFrom(msg.sender, address(this), amount2));
totalShares = shares[msg.sender] = amount1;
_sync();
}
modifier notPaused() {
require(!paused);
_;
}
function pause() external {
require(msg.sender == owner);
paused = true;
}
function unpause() external {
require(msg.sender == owner);
paused = false;
_sync();
}
function swap(IERC20 tokenIn, uint256 amountIn, IERC20 tokenOut, uint256 amountOut) external notPaused() {
require((tokenIn == token1 && tokenOut == token2) || (tokenIn == token2 && tokenOut == token1));
require(tokenIn.transferFrom(msg.sender, address(this), amountIn));
require(tokenOut.transfer(msg.sender, amountOut));
uint256 x = tokenIn .balanceOf(address(this));
uint256 y = tokenOut.balanceOf(address(this));
require(1000 * x * y - amountIn * y * 5 >= 1000 * k, "bad swap"); // charge fee, (x - 0.005 * amountIn) * y >= k
_sync();
}
function addLiquidity(uint256 sharesToMint) external notPaused() {
// amount / balanceOf == sharesToMint / totalShares
uint256 amount1 = token1.balanceOf(address(this)) * sharesToMint / totalShares;
uint256 amount2 = token2.balanceOf(address(this)) * sharesToMint / totalShares;
shares[msg.sender] += sharesToMint;
totalShares += sharesToMint;
require(token1.transferFrom(msg.sender, address(this), amount1));
require(token2.transferFrom(msg.sender, address(this), amount2));
_sync();
}
function removeLiquidity(uint256 sharesToBurn) external {
uint256 amount1 = token1.balanceOf(address(this)) * sharesToBurn / totalShares;
uint256 amount2 = token2.balanceOf(address(this)) * sharesToBurn / totalShares;
shares[msg.sender] -= sharesToBurn;
totalShares -= sharesToBurn;
require(token1.transfer(msg.sender, amount1));
require(token2.transfer(msg.sender, amount2));
_sync();
}
function flashLoan(IERC3156FlashBorrower receiver, IERC20 token, uint256 amount, bytes calldata data) external notPaused() returns (bool) {
require(token == token1 || token == token2);
uint256 fee = amount * 5 / 1000; // 0.5 %
require(token.transfer(address(receiver), amount));
require(receiver.onFlashLoan(msg.sender, address(token), amount, fee, data) == keccak256("ERC3156FlashBorrower.onFlashLoan"));
require(IERC20(token).transferFrom(address(receiver), address(this), amount + fee));
_sync();
return true;
}
function _sync() internal {
k = token1.balanceOf(address(this)) * token2.balanceOf(address(this));
}
}
interface IERC3156FlashBorrower {
function onFlashLoan(address initiator, address token, uint256 amount, uint256 fee, bytes calldata data) external returns (bytes32);
}```