contract DigitalMarketDexLogic is DigitalMarketStorage{
    
    function deposit() public payable deprecable {
        require(!paused, "Platform is temporary paused!");
        require(msg.value>0, "amount must be greater than zero!");
        require(blacklist[msg.sender] != true, "address is blacklisted!");
        
        tokens[address(0)][msg.sender] = SafeMath.safeAdd(tokens[address(0)][msg.sender], msg.value);
        emit Deposit(address(0), msg.sender, msg.value, tokens[address(0)][msg.sender]); 
    }

    function withdraw(uint256 _amount) public pausable{
        require(tokens[address(0)][msg.sender] >= _amount, "insufecint balance");
        require(blacklist[msg.sender] != true, "address is blacklisted!");
        require(_amount>0, "amount must be greater than zero!");
        
        tokens[address(0)][msg.sender] = SafeMath.safeSub(tokens[address(0)][msg.sender], _amount);
        if (!msg.sender.send(_amount)) {
            revert("Transfer has failed");
        }
        emit Withdraw(address(0), msg.sender, _amount, tokens[address(0)][msg.sender]);
    }
  
    function receiveApproval(address _spender, uint _amount, address _reciver) public payable deprecable {
        address spender = _spender;
        address tokenAdd = msg.sender;
        require(_reciver == address(this), "approve is not recieved!");
        require(!paused, "Platform is temporary paused!");
        require(blacklist[_spender] != true, "address is blacklisted!");
        require(_amount>0, "amount must be greater than zero!");
        
        if (!Token(tokenAdd).transferFrom(spender, address(this), _amount)) {
          revert("Transfer has failed");
        }
        
        tokens[tokenAdd][spender] = SafeMath.safeAdd(tokens[tokenAdd][spender], _amount);
        emit Deposit(tokenAdd, spender, _amount, tokens[tokenAdd][spender]);
    }

    function withdrawToken(address _token, uint256 _amount) public pausable{
        require(_token != address(0), "Can't withdraw ETH by this Function");
        require(tokens[_token][msg.sender] >= _amount, "insuficient balance!");
        require(blacklist[msg.sender] != true, "address is blacklisted!");
        require(_amount>0, "amount must be greater than zero!");
        
        tokens[_token][msg.sender] = SafeMath.safeSub(tokens[_token][msg.sender], _amount);
        if (!Token(_token).transfer(msg.sender, _amount)) {
          revert("Transfer has failed");
        }
        emit Withdraw(_token, msg.sender, _amount, tokens[_token][msg.sender]);
    }
    
    function checkGiver(address _giver, bytes32 _h, bytes memory signature) private pure returns (bool){
        address signer = recoverSigner(keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", _h)), signature);
        if(signer == _giver){ return true;} else {return false;}
        
    }
    
    function tradeMultiTransaction(address[] memory _giver, uint[] memory _gAmount, address[] memory _gToken, uint[] memory _tAmount, address[] memory _tToken,  uint[] memory _expires, uint[] memory _nonce, uint[] memory _amount, bytes memory signature, bytes memory signature2, bytes memory signature3) public protocolConnection {
      require(!paused, "Platform is temporary paused!");
      for(uint i = 0; i<_giver.length; i++){
            if(i==0){
               tradeMulti(_giver[i], msg.sender, _gAmount[i], _gToken[i], _tAmount[i], _tToken[i], _expires[i], _nonce[i], _amount[i], signature);
            } else if(i==1){
                tradeMulti(_giver[i], msg.sender, _gAmount[i], _gToken[i], _tAmount[i], _tToken[i], _expires[i], _nonce[i], _amount[i], signature2);
            } else if(i==2){
                tradeMulti(_giver[i], msg.sender, _gAmount[i], _gToken[i], _tAmount[i], _tToken[i], _expires[i], _nonce[i], _amount[i], signature3);
            } 
       }
    }
    
    function tradeMulti
	        (address _giver, address _taker, uint _gAmount, address _gToken,
	        uint _tAmount, address _tToken,  uint _expires, uint _nonce, uint _amount,
	        bytes memory signature) 
	        private
            {
      
      bytes32 h = keccak256(abi.encodePacked(this, _gToken, _gAmount, _tToken, _tAmount, _expires, _nonce)); 
     
      require(checkGiver(_giver, h, signature)==true, "correct giver not found!"); 
      require(tokens[_gToken][_giver] >= (_gAmount - orderFills[_giver][h]), "Giver insuficient balance!"); 
      require(SafeMath.safeAdd(orderFills[_giver][h], _amount) <= _gAmount, "order amount isn't enough!");
	  require(tokens[_tToken][_taker] >=  (SafeMath.safeMul(_tAmount, _amount) / _gAmount), "Takeer insuficient balance"); 
	  require(block.number < _expires, "Open order has expired!"); 
	  
	  // call and do the exchange  
      tradeBalances(_tToken, (SafeMath.safeMul(_tAmount, _amount) / _gAmount), _gToken, _amount, _giver, _taker);
	  orderFills[_giver][h] = SafeMath.safeAdd(orderFills[_giver][h], _amount);
	  
	  // update volume information [for protocol purpose]
	  platformVolume[_gToken] = SafeMath.safeAdd(platformVolume[_gToken], _amount);
	  tradersVolume[_gToken][_giver] = SafeMath.safeAdd(tradersVolume[_gToken][_giver], _amount);
	  tradersVolume[_tToken][msg.sender] = SafeMath.safeAdd(tradersVolume[_tToken][msg.sender], (SafeMath.safeMul(_tAmount, _amount) / _gAmount));
	  platformVolume[_tToken] = SafeMath.safeAdd(platformVolume[_tToken], (SafeMath.safeMul(_tAmount, _amount) / _gAmount));
	  
	  if(_gToken == address(0)){ // for converting rate purpose 
	      tradersVolumeRated[_tToken][msg.sender] = SafeMath.safeAdd(tradersVolumeRated[_tToken][msg.sender], _amount);
	      platformVolumeRatd[_tToken] = SafeMath.safeAdd(platformVolumeRatd[_tToken], _amount); // _gAmount
	  } else {
	      tradersVolumeRated[_gToken][_giver] = SafeMath.safeAdd(tradersVolumeRated[_gToken][_giver], (SafeMath.safeMul(_tAmount, _amount) / _gAmount));
	      platformVolumeRatd[_gToken] = SafeMath.safeAdd(platformVolumeRatd[_gToken], (SafeMath.safeMul(_tAmount, _amount) / _gAmount)); 
	  }
	  
	  emit Trade(_gToken, _gAmount, _tToken, _tAmount, _giver, _taker, _nonce);
	  
    }
    
	
	function trade
	        (address _giver, uint _gAmount, address _gToken,
	        uint _tAmount, address _tToken,  uint _expires, uint _nonce, uint _amount,
	        bytes memory signature) 
	        public protocolConnection
            {
      require(!paused, "Platform is temporary paused!"); 
      bytes32 h = keccak256(abi.encodePacked(this, _gToken, _gAmount, _tToken, _tAmount, _expires, _nonce)); 
      address signer = recoverSigner(keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", h)), signature);
      
      require (signer == _giver, "Order signiture Error");
      require(tokens[_gToken][_giver] >= (_gAmount - orderFills[_giver][h]), "Giver insuficient balance!"); 
      require(SafeMath.safeAdd(orderFills[_giver][h], _amount) <= _gAmount, "order amount isn't enough!");
	  require(tokens[_tToken][msg.sender] >=  (SafeMath.safeMul(_tAmount, _amount) / _gAmount), "Taker insuficient balance"); 
	  require(block.number < _expires, "Open order has expired!"); 
	  
	  // call and do the exchange  
      tradeBalances(_tToken, (SafeMath.safeMul(_tAmount, _amount) / _gAmount), _gToken, _amount, _giver, msg.sender);
	  orderFills[_giver][h] = SafeMath.safeAdd(orderFills[_giver][h], _amount);
	  
	  // update volume information [for protocol purpose]
	  platformVolume[_gToken] = SafeMath.safeAdd(platformVolume[_gToken], _amount);
	  tradersVolume[_gToken][_giver] = SafeMath.safeAdd(tradersVolume[_gToken][_giver], _amount);
	  tradersVolume[_tToken][msg.sender] = SafeMath.safeAdd(tradersVolume[_tToken][msg.sender], (SafeMath.safeMul(_tAmount, _amount) / _gAmount));
	  platformVolume[_tToken] = SafeMath.safeAdd(platformVolume[_tToken], (SafeMath.safeMul(_tAmount, _amount) / _gAmount)); 
	  
	  if(_gToken == address(0)){ // for converting rate purpose 
	      tradersVolumeRated[_tToken][msg.sender] = SafeMath.safeAdd(tradersVolumeRated[_tToken][msg.sender], _amount);
	      platformVolumeRatd[_tToken] = SafeMath.safeAdd(platformVolumeRatd[_tToken], _amount); 
	  } else {
	      tradersVolumeRated[_gToken][_giver] = SafeMath.safeAdd(tradersVolumeRated[_gToken][_giver], (SafeMath.safeMul(_tAmount, _amount) / _gAmount));
	      platformVolumeRatd[_gToken] = SafeMath.safeAdd(platformVolumeRatd[_gToken], (SafeMath.safeMul(_tAmount, _amount) / _gAmount)); 
	  }
	  
	  emit Trade(_gToken, _gAmount, _tToken, _tAmount, _giver, msg.sender, _nonce);
	}
    
    function tradeBalances(address _tToken, uint _tAmount, address _gToken, uint _gAmount,
            address _giver, address _caller
            ) private {
     // Calucate the amount for each one
	  uint gNet = SafeMath.safeSub(_gAmount, SafeMath.safeDiv(_gAmount, (100/fee)) / feeRate);
	  uint tNet = SafeMath.safeSub(_tAmount, SafeMath.safeDiv(_tAmount, (100/fee)) / feeRate);// (100/(fee/1)))) / feeRate; 
	  // Transfering the amount Between users
	  //Giver
	  tokens[_gToken][_giver] = SafeMath.safeSub(tokens[_gToken][_giver], _gAmount); // All amount
	  tokens[_tToken][_giver] = SafeMath.safeAdd(tokens[_tToken][_giver], tNet); // Net amount of tNet
	  // Taker
	  tokens[_tToken][_caller] = SafeMath.safeSub(tokens[_tToken][_caller], _tAmount); // All amount
	  tokens[_gToken][_caller] = SafeMath.safeAdd(tokens[_gToken][_caller], gNet); // Net amount of gAmount
	  // Fee
	  tokens[_gToken][protocolAddress] = SafeMath.safeAdd(tokens[_gToken][protocolAddress], SafeMath.safeDiv(_gAmount, (100/fee))/ feeRate); // feeAccount
	  tokens[_tToken][protocolAddress] = SafeMath.safeAdd(tokens[_tToken][protocolAddress], SafeMath.safeDiv(_tAmount, (100/fee))/ feeRate); // feeAccount
	  
	  platformFees[_gToken] = SafeMath.safeAdd(platformFees[_gToken], SafeMath.safeDiv(_gAmount, (100/fee))/ feeRate);
	  platformFees[_tToken] = SafeMath.safeAdd(platformFees[_tToken], SafeMath.safeDiv(_tAmount, (100/fee))/ feeRate);
	  
	  if (tradeTracker != address(0)) {
        TradeTrackerInterface(tradeTracker).tradeComplete(_tToken, _tAmount, _gToken, _gAmount, _giver, _caller, SafeMath.safeDiv(_tAmount, 100), 0); 
      }
	}
	
	function cancelOrder
	        (address _giver, uint _gAmount, address _gToken, uint _tAmount, address _tToken,  
	        uint _expires, uint _nonce, uint _amount,  bytes memory signature)  public  
	        {
         require(!paused, "Platform is temporary paused!");
         bytes32 h = keccak256(abi.encodePacked(this, _gToken, _gAmount, _tToken, _tAmount, _expires, _nonce)); 
         address signer = recoverSigner(keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", h)), signature);
         
         require (signer == _giver, "Order signiture Error!");
         require (_giver == msg.sender, "user not premissoned!");
         uint FilledBefore = orderFills[_giver][h];
         //orderFills[_giver][h] = SafeMath.safeAdd(orderFills[_giver][h], (_amount - FilledBefore));
         orderFills[_giver][h] = _gAmount; // 100% of amount and prevent Java Bignumbers Errors 
         emit Cancel(_gToken, _gAmount, _tToken, _tAmount, _expires, _nonce, msg.sender);
    }
    
    // Signitures 
    function splitSignature(bytes memory sig) internal pure returns (uint8 v, bytes32 r, bytes32 s) {
        require(sig.length == 65, "Signiture Error!");
        assembly {
            // first 32 bytes, after the length prefix.
            r := mload(add(sig, 32))
            // second 32 bytes.
            s := mload(add(sig, 64))
            // final byte (first byte of the next 32 bytes).
            v := byte(0, mload(add(sig, 96)))
        }
        return (v, r, s);
    }
    
    function recoverSigner(bytes32 message, bytes memory sig) internal pure returns (address) {
        (uint8 v, bytes32 r, bytes32 s) = splitSignature(sig);
        return ecrecover(message, v, r, s);
    }
    
}
function splitSignature(bytes memory sig) internal pure returns (uint8 v, bytes32 r, bytes32 s) {
    require(sig.length == 65, "Signiture Error!");
    assembly {
        // first 32 bytes, after the length prefix.
        r := mload(add(sig, 32))
        // second 32 bytes.
        s := mload(add(sig, 64))
        // final byte (first byte of the next 32 bytes).
        v := byte(0, mload(add(sig, 96)))
    }
    return (v, r, s);
}

// Signitures 