UniswapV1 In Solidity

Factory工厂合约

pragma solidity ^0.8.0;

contract Factory {
    mapping(address => address) tokenToExchange;
    mapping(address => address) exchange_to_token;

    function createExchange(address _token) public returns (address) {
        require(_token != address(0), "Invalid token address");
        require(
            tokenToExchange[_token] == address(0),
            "Exchange already registered."
        );
        Exchange exchange = new Exchange(_token);

        tokenToExchange[_token] = address(exchange);
        exchange_to_token[address(exchange)] = _token;

        return address(exchange);
    }

    function getExchange(address _token) public view returns (address) {
        return tokenToExchange[_token];
    }

    function getToken(address exchange) public view returns (address) {
        return exchange_to_token[exchange];
    }
}

Exchange

pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

interface IExchange {
    function ethToTokenSwap(uint256 _minTokens, address recipient)
    external
    payable;

    function ethToTokenTransfer(uint256 _minTokens, address _recipient)
    external
    payable;
}

interface IFactory {
    function getExchange(address _tokenAddress) external returns (address);

    function getToken(address exchange) external returns (address);
}

contract Exchange is ERC20 {
    address public tokenAddress;
    address public factoryAddress;

    constructor(address _token) ERC20("Uniswap-V1", "UNI1") {
        require(_token != address(0), "invalid token address");

        tokenAddress = _token;
        factoryAddress = msg.sender;
    }

    function addLiquidity(
        uint256 min_liquidity,
        uint256 max_tokens,
        uint256 deadline
    ) public payable {
        require(deadline > block.timestamp);
        require(msg.value > 0 && max_tokens > 0);
        uint256 total_liquidity = totalSupply();
        IERC20 token = IERC20(tokenAddress);
        if (total_liquidity == 0) {
            require(
                factoryAddress != address(0) &&
                tokenAddress != address(0) &&
                msg.value > 1 gwei
            );
            address exceptedExchange = IFactory(factoryAddress).getExchange(
                tokenAddress
            );
            require(exceptedExchange == address(this));
            token.transferFrom(msg.sender, address(this), max_tokens);
            uint256 liquidity = address(this).balance;
            _mint(msg.sender, liquidity);
        } else {
            require(min_liquidity > 0);
            uint256 ethReserve = address(this).balance - msg.value;
            uint256 tokenReserve = getReserve();
            uint256 _minTokenAmount = (msg.value * tokenReserve) /
                        ethReserve +
                        1; //向上取整
            uint256 liquidity = (totalSupply() * msg.value) / ethReserve;
            require(
                max_tokens >= _minTokenAmount && liquidity >= min_liquidity,
                "Insufficient token for liquidity"
            );

            token.transferFrom(msg.sender, address(this), _minTokenAmount);
            _mint(msg.sender, liquidity);
        }
    }

    function removeLiquidity(
        uint256 amount,
        uint256 min_eth,
        uint256 min_tokens,
        uint256 deadline
    ) public returns (uint256, uint256) {
        require(amount > 0, "Invalid amount");
        require(deadline > block.timestamp);
        uint256 ethAmount = (amount * address(this).balance) / totalSupply();
        uint256 tokenAmount = (amount * getReserve()) / totalSupply();

        require(ethAmount >= min_eth && tokenAmount >= min_tokens);
        _burn(msg.sender, amount);

        IERC20(tokenAddress).transfer(msg.sender, tokenAmount);
        payable(msg.sender).transfer(ethAmount);

        return (ethAmount, tokenAmount);
    }

    function getReserve() public view returns (uint256) {
        return IERC20(tokenAddress).balanceOf(address(this));
    }

    // Swap fee: 3%
    // 通过存入的资产计算能够兑换到的资产数量
    function getInputPrice(
        uint256 input_amount,
        uint256 inReserve,
        uint256 outReserve
    ) public pure returns (uint256) {
        require(inReserve > 0 && outReserve > 0);
        uint256 inputAmountWithFee = input_amount * 997;
        uint256 numerator = outReserve * inputAmountWithFee;
        uint256 denominator = inReserve * 1000 + inputAmountWithFee;
        return numerator / denominator;
    }

    // 通过卖出资产计算能够兑换到的资产数量
    function getOutputPrice(
        uint256 output_amount,
        uint256 inReserve,
        uint256 outReserve
    ) public pure returns (uint256) {
        require(inReserve > 0 && outReserve > 0);
        uint256 numerator = inReserve * output_amount * 1000;
        uint256 denominator = 997 * (outReserve - output_amount);
        return numerator / denominator + 1;
    }

    function getTokenAmount(uint256 _ethSold) public view returns (uint256) {
        require(_ethSold > 0, "Invalid Amount");

        return getInputPrice(_ethSold, address(this).balance, getReserve());
    }

    function getEthAmount(uint256 _tokenSold) public view returns (uint256) {
        require(_tokenSold > 0, "Invalid Amount");

        return getInputPrice(_tokenSold, getReserve(), address(this).balance);
    }

    function ethToTokenSwap(uint256 _minToken, address recipient)
    public
    payable
    {
        uint256 tokenAmount = getInputPrice(
            msg.value,
            address(this).balance - msg.value,
            getReserve()
        );
        require(tokenAmount >= _minToken, "Insufficient token amount");

        IERC20 token = IERC20(tokenAddress);
        token.transfer(recipient, tokenAmount);
    }

    function tokenToEthSwap(uint256 _minEth, uint256 _tokenSold) public {
        uint256 ethAmount = getEthAmount(_tokenSold);

        require(ethAmount >= _minEth, "Insufficient eth amount");

        IERC20(tokenAddress).transferFrom(
            msg.sender,
            address(this),
            _tokenSold
        );
        payable(msg.sender).transfer(ethAmount);
    }

    function tokenToTokenSwap(
        uint256 tokens_sold,
        uint256 min_tokens_bought,
        uint256 min_eth_bought,
        uint256 _minBoughtTokenAmount,
        address _anotherToken,
        address recipient
    ) public {
        address exchangeAddress = IFactory(factoryAddress).getExchange(
            _anotherToken
        );
        require(exchangeAddress != address(0), "This token has no exchange.");

        uint256 tokenReserve = getReserve();
        uint256 ethBought = getInputPrice(
            tokens_sold,
            tokenReserve,
            address(this).balance
        );
        require(ethBought >= min_eth_bought);

        IERC20(tokenAddress).transferFrom(
            msg.sender,
            address(this),
            tokens_sold
        );

        IExchange(exchangeAddress).ethToTokenSwap{value: ethBought}(
            _minBoughtTokenAmount,
            recipient
        );
    }
}

Tokens

pragma solidity ^0.8.0;

contract Token is ERC20 {
    constructor(
        string memory name,
        string memory symbol,
        uint256 initialSupply
    ) ERC20(name, symbol) {
        _mint(msg.sender, initialSupply);
    }
}