2613 large

Uniswap 的 Solidity 版本 (有小改动)

thishe · 于 发布 · 380 次阅读

这两天看Uniswap的合约源代码,感觉不太习惯,就改了一版Solidity版本。改的过程中发现Token1/Token2这种非ETH交易对需要执行两次,我改为采用 TokenX/BaseToken这种模式,也就是一个合约可以处理一个交易市场,例如BaseToken是USDT,那么交易对就可以有ETH/USDT,ABC/USDT,BN/USDT,等。流动性提供者收取手续费为千分之一。未测试,请勿商用,仅用于学习爱好交流。

pragma solidity ^0.6.4;

//https://github.com/OpenZeppelin/zeppelin-solidity/blob/master/contracts/math/SafeMath.sol
library SafeMath {
  function mul(uint256 a, uint256 b) internal pure returns (uint256 c) {
    if (a == 0) {
      return 0;
    }
    c = a * b;
    assert(c / a == b);
    return c;
  }

  function div(uint256 a, uint256 b) internal pure returns (uint256) {
    return a / b;
  }

  function sub(uint256 a, uint256 b) internal pure returns (uint256) {
    assert(b <= a);
    return a - b;
  }

  function add(uint256 a, uint256 b) internal pure returns (uint256 c) {
    c = a + b;
    assert(c >= a);
    return c;
  }
}


interface IErc20Token {
    function totalSupply() external view returns (uint);
    function balanceOf(address tokenOwner) external view returns (uint balance);
    function allowance(address tokenOwner, address spender) external view returns (uint remaining);
    function transfer(address to, uint tokens) external returns (bool success);
    function approve(address spender, uint tokens) external returns (bool success);
    function transferFrom(address from, address to, uint tokens) external returns (bool success);
    event Transfer(address indexed from, address indexed to, uint tokens);
    event Approval(address indexed tokenOwner, address indexed spender, uint tokens);
}


//支付相关的接口
interface _IPay
{
    function deposit() payable external returns (bool);
    function depositToken(address _token, uint _amount) external returns (bool);

    function withdraw(uint _amount) external returns (bool);
    function withdrawToken(address _token, uint _amount) external returns (bool);
}

contract BasePay is _IPay {    
    using SafeMath for uint;
    uint createTime = now;
    uint64 public currentEventId = 1;
    function getEventId() internal returns(uint64 _result) {
        _result = currentEventId;
        currentEventId ++;
    }

    mapping (address => mapping (address => uint)) public tokenUserAmountOf;

    event OnDeposit (address indexed _token, address indexed _user, uint _amount, uint _balance, uint64 _eventId);
    event OnWithdraw(address indexed _token, address indexed _user, uint _amount, uint _balance, uint64 _eventId);

    function deposit() payable override external  returns (bool){
        return _deposit();
    }

    function _deposit() internal  returns (bool){
        tokenUserAmountOf[address(0x0)][msg.sender] = tokenUserAmountOf[address(0x0)][msg.sender].add(msg.value);
        emit OnDeposit(address(0x0), msg.sender, msg.value, tokenUserAmountOf[address(0x0)][msg.sender], getEventId());
        return true;
    }

    function withdraw(uint _amount) override external  returns (bool) {
        require(_amount > 0);
        require(tokenUserAmountOf[address(0x0)][msg.sender] >= _amount);
        tokenUserAmountOf[address(0x0)][msg.sender] = tokenUserAmountOf[address(0x0)][msg.sender].sub(_amount);
        msg.sender.transfer(_amount);
        emit OnWithdraw(address(0x0), msg.sender, _amount, tokenUserAmountOf[address(0x0)][msg.sender], getEventId());
        return true;
    }

    function depositToken(address _token, uint _amount) override external  returns (bool) {
        //remember to call Token(address).approve(this, amount) or this contract will not be able to do the transfer on your behalf.
        require(_token != address(0x0));

        //TODO: 要注意这两种写法 有些token 没有 returns , 例如 usdt
        // require(IErc20Token(_token).transferFrom(msg.sender, address(this), _amount));
        IErc20Token(_token).transferFrom(msg.sender, address(this), _amount);

        tokenUserAmountOf[_token][msg.sender] = tokenUserAmountOf[_token][msg.sender].add(_amount);
        emit OnDeposit(_token, msg.sender, _amount, tokenUserAmountOf[_token][msg.sender], getEventId());
        return true;
    }

    function withdrawToken(address _token, uint _amount) override external  returns (bool) {
        require(_amount > 0);
        require(_token != address(0x0));
        require (tokenUserAmountOf[_token][msg.sender] >= _amount);
        tokenUserAmountOf[_token][msg.sender] = tokenUserAmountOf[_token][msg.sender].sub(_amount);

        //TODO: 要注意这两种写法,理论上下面的写法更安全,但是有的token不标准,其function没有返回参数,例如usdt,这会出问题的,所以这里需要测试所有的token合约,
        IErc20Token(_token).transfer(msg.sender, _amount);
        // require(IErc20Token(_token).transfer(msg.sender, _amount));

        emit OnWithdraw(_token, msg.sender, _amount, tokenUserAmountOf[_token][msg.sender], getEventId());
        return true;
    }

}

contract Exchange is BasePay {
    mapping(address => uint)  public tokenLiquidityOf;                          //总的流动性,币对 token1 地址,流动性值,
    mapping(address => mapping(address => uint))  public userTokenLiquidityOf;  //用户的流动性,币对token1 地址,用户地址,流动性值
    mapping(address => bool)  public tokenOf;                                   //币对 token1
    address public baseToken;                                                   //交易市场,所有的token都是token1,这个baseToken是 token2, 所有的交易对都是 token1 / token2 的模式。
    uint public minAddBaseTokenAmount;                                          // 增加流动性,最少 baseToken 书量。
    mapping(address => uint)  public tokenAmountOf;           //币对 token1 总金额
    mapping(address => uint)  public baseTokenAmountOf;       //币对 baseToken 总金额

    address public owner;
    modifier onlyOwner {
        require(msg.sender == owner);
        _;
    } 

    function setOwner(address _newOwner)  public  onlyOwner {
        owner = _newOwner;
    }

    constructor(address _baseToken, uint _minAddBaseTokenAmount)  public {
        require(_baseToken != address(0x0));
        baseToken = _baseToken;

        minAddBaseTokenAmount = _minAddBaseTokenAmount;
    }

    function setTokenPair(address token, bool isOpened) public onlyOwner {
      tokenOf[token] = isOpened;
    }

    event OnAddLiquidity(address user, uint baseTokenAmount, address token, uint256 tokenAmount, uint eventId);

    function addLiquidity(uint baseTokenAmount, address token, uint256 maxTokenAmount, uint deadline) public returns (uint256) {
      require(tokenOf[token]);
      require(baseTokenAmount > 0);
      require(maxTokenAmount > 0);
      require(deadline > block.timestamp);
      require(baseTokenAmount >= minAddBaseTokenAmount);

      if(tokenLiquidityOf[token] == 0) {
        tokenUserAmountOf[baseToken][msg.sender] =  tokenUserAmountOf[baseToken][msg.sender].sub(baseTokenAmount);
        tokenAmountOf[baseToken] = tokenAmountOf[baseToken] + baseTokenAmount;

        tokenUserAmountOf[token][msg.sender] =   tokenUserAmountOf[token][msg.sender].sub(maxTokenAmount);
        tokenAmountOf[token] = tokenAmountOf[token].add(maxTokenAmount);

         uint Liquidity = 1 ether;            //baseTokenAmount; 
        tokenLiquidityOf[token] = Liquidity;
        userTokenLiquidityOf[msg.sender][token] = userTokenLiquidityOf[msg.sender][token].add(Liquidity);

        emit OnAddLiquidity(msg.sender, baseTokenAmount, token, maxTokenAmount, getEventId());
        return Liquidity;
      }
      else {
        uint TokenAmount = baseTokenAmount * tokenAmountOf[token] / tokenAmountOf[baseToken] + 1;
        require(TokenAmount <= maxTokenAmount);

        uint Liquidity = baseTokenAmount *  tokenLiquidityOf[token] / tokenAmountOf[baseToken];
        tokenLiquidityOf[token] = tokenLiquidityOf[token].add(Liquidity);
        userTokenLiquidityOf[msg.sender][token] = userTokenLiquidityOf[msg.sender][token].add(Liquidity);

        tokenUserAmountOf[baseToken][msg.sender] =  tokenUserAmountOf[baseToken][msg.sender].sub(baseTokenAmount);
        tokenAmountOf[baseToken] = tokenAmountOf[baseToken].add(baseTokenAmount);

        tokenUserAmountOf[token][msg.sender] =   tokenUserAmountOf[token][msg.sender].sub(TokenAmount);
        tokenAmountOf[token] = tokenAmountOf[token].add(TokenAmount);

        emit OnAddLiquidity(msg.sender, baseTokenAmount, token, TokenAmount, getEventId());
        return Liquidity;
      }
    }

    event OnRemoveLiquidity(address user, uint baseTokenAmount, address token, uint256 tokenAmount, uint eventId);

    function removeLiquidity(uint baseTokenAmount, address token, uint256 minTokenAmount, uint deadline) public
        returns (uint256 tokenAmount) 
    {
        //require(tokenOf[token]);  //no
        require(baseTokenAmount > 0);
        require(baseTokenAmount < tokenAmountOf[baseToken] );
        require(minTokenAmount > 0);
        require(deadline > block.timestamp);

        uint Liquidity = userTokenLiquidityOf[msg.sender][token];
        uint CanBaseToken = Liquidity  * tokenAmountOf[baseToken] /  tokenLiquidityOf[token];
        require(baseTokenAmount <= CanBaseToken);

        uint CanGetToken = baseTokenAmount  * tokenAmountOf[token] /   tokenAmountOf[baseToken];
        require(minTokenAmount >= CanGetToken);

        uint ChangedLiquidity = userTokenLiquidityOf[msg.sender][token] - baseTokenAmount * tokenLiquidityOf[token]  / tokenAmountOf[baseToken] ;

        tokenLiquidityOf[token] = tokenLiquidityOf[token].sub(ChangedLiquidity);
        userTokenLiquidityOf[msg.sender][token] = userTokenLiquidityOf[msg.sender][token].sub(ChangedLiquidity);

        tokenUserAmountOf[baseToken][msg.sender] =  tokenUserAmountOf[baseToken][msg.sender].add(baseTokenAmount);
        tokenAmountOf[baseToken] = tokenAmountOf[baseToken].sub(baseTokenAmount);

        tokenUserAmountOf[token][msg.sender] =   tokenUserAmountOf[token][msg.sender].add(CanGetToken);
        tokenAmountOf[token] = tokenAmountOf[token].sub(CanGetToken);

        emit OnRemoveLiquidity(msg.sender, baseTokenAmount,  token, CanGetToken, getEventId());
        return CanGetToken;
    }

    function getBaseTokenPriceAmount(address token, uint buyTokenAmount) public view returns (uint) {
        require(tokenOf[token]);
        require(tokenAmountOf[token] > buyTokenAmount);
        uint BuyBaseTokenAmount = tokenAmountOf[token] * tokenAmountOf[baseToken] * 999 /  (tokenAmountOf[token]  - buyTokenAmount) / 1000;
        return BuyBaseTokenAmount;
    }

    function getokenPriceAmount(address token, uint buyBaseTokenAmount) public view returns (uint) {
        require(tokenOf[token]);
        require(tokenAmountOf[baseToken] > buyBaseTokenAmount);
        uint BuyTokenAmount = tokenAmountOf[token] * tokenAmountOf[baseToken]  * 999 /  (tokenAmountOf[baseToken]  - buyBaseTokenAmount) / 1000;
        return BuyTokenAmount;
    }

    event OnBuyToken(address user, uint baseTokenAmount,  address token, uint tokenAmount,  uint eventId); 

    function buyToken(uint baseTokenAmount,  address token, uint minTokenAmount,  uint deadline) public returns (uint) {
          require(tokenOf[token]);
          require(baseTokenAmount > 0);
          require(minTokenAmount > 0);
          require(deadline > block.timestamp);

          uint CanTokenAmount = getokenPriceAmount(token, baseTokenAmount);
          require(CanTokenAmount >= minTokenAmount);

          tokenUserAmountOf[baseToken][msg.sender] =  tokenUserAmountOf[baseToken][msg.sender].add(baseTokenAmount);
          tokenAmountOf[baseToken] = tokenAmountOf[baseToken].sub(baseTokenAmount);

          tokenUserAmountOf[token][msg.sender] =   tokenUserAmountOf[token][msg.sender].sub(CanTokenAmount);
          tokenAmountOf[token] = tokenAmountOf[token].add(CanTokenAmount);

          emit OnBuyToken(msg.sender, baseTokenAmount,   token,  CanTokenAmount,  getEventId());
          return CanTokenAmount;
    }

    event OnBuyBaseToken(address user, uint baseTokenAmount,  address token, uint tokenAmount,  uint eventId); 

    function buyBaseToken(uint minBaseTokenAmount,  address token, uint tokenAmount,  uint deadline) public returns (uint) {
          require(tokenOf[token]);
          require(minBaseTokenAmount > 0);
          require(tokenAmount > 0);
          require(deadline > block.timestamp);

          uint CanBaseTokenAmount = getBaseTokenPriceAmount(token, tokenAmount);
          require(CanBaseTokenAmount >= minBaseTokenAmount);

          tokenUserAmountOf[baseToken][msg.sender] =  tokenUserAmountOf[baseToken][msg.sender].add(CanBaseTokenAmount);
          tokenAmountOf[baseToken] = tokenAmountOf[baseToken].sub(CanBaseTokenAmount);

          tokenUserAmountOf[token][msg.sender] =   tokenUserAmountOf[token][msg.sender].sub(tokenAmount);
          tokenAmountOf[token] = tokenAmountOf[token].add(tokenAmount);

          emit OnBuyBaseToken(msg.sender, CanBaseTokenAmount,   token,  tokenAmount,  getEventId());
          return tokenAmount;
    }

    receive() external payable {                       
        if (msg.value > 0){
            tokenUserAmountOf[address(0x0)][address(this)] =  tokenUserAmountOf[address(0x0)][address(this)]  + msg.value;      
        }
    }

    fallback() external {
        assert(1 == 2);
    }


}
  • 暂无回复。