func: function futuresTrade(
          uint8[2] v,
          bytes32[4] rs,
          uint256[8] tradeValues,
          address[2] tradeAddresses,
          bool takerIsBuying,
          bytes32 futuresContractHash
      ) onlyAdmin returns (uint filledTakerTokenAmount)
      {
  
          
  
          FuturesOrderPair memory t  = FuturesOrderPair({
              makerNonce      : tradeValues[0],
              takerNonce      : tradeValues[1],
              takerGasFee     : tradeValues[2],
              takerIsBuying   : tradeValues[3],
              makerAmount     : tradeValues[4],      
              takerAmount     : tradeValues[5],   
              makerPrice      : tradeValues[6],         
              takerPrice      : tradeValues[7],
  
              maker           : tradeAddresses[0],
              taker           : tradeAddresses[1],
  
              
              makerOrderHash  : keccak256(this, futuresContractHash, tradeAddresses[0], tradeValues[4], tradeValues[6], !takerIsBuying, tradeValues[0]),
              takerOrderHash  : keccak256(this, futuresContractHash, tradeAddresses[1], tradeValues[5], tradeValues[7],  takerIsBuying, tradeValues[1]),            
  
              futuresContract : futuresContractHash,
  
              baseToken       : futuresAssets[futuresContracts[futuresContractHash].asset].baseToken,
              floorPrice      : futuresContracts[futuresContractHash].floorPrice,
              capPrice        : futuresContracts[futuresContractHash].capPrice,
  
              
              makerPositionHash           : keccak256(this, tradeAddresses[0], futuresContractHash, !takerIsBuying),
              makerInversePositionHash    : keccak256(this, tradeAddresses[0], futuresContractHash, takerIsBuying),
  
              takerPositionHash           : keccak256(this, tradeAddresses[1], futuresContractHash, takerIsBuying),
              takerInversePositionHash    : keccak256(this, tradeAddresses[1], futuresContractHash, !takerIsBuying)
  
          });
  
  
      
          
          if (!validateUint128(t.makerAmount) || !validateUint128(t.takerAmount) || !validateUint64(t.makerPrice) || !validateUint64(t.takerPrice))
          {            
              emit LogError(uint8(Errors.UINT48_VALIDATION), t.makerOrderHash, t.takerOrderHash);
              return 0; 
          }
  
  
          
          if (block.number > futuresContracts[t.futuresContract].expirationBlock || futuresContracts[t.futuresContract].closed == true || futuresContracts[t.futuresContract].broken == true)
          {
              emit LogError(uint8(Errors.FUTURES_CONTRACT_EXPIRED), t.makerOrderHash, t.takerOrderHash);
              return 0; 
          }
  
          
          if (ecrecover(keccak256("\x19Ethereum Signed Message:\n32", t.makerOrderHash), v[0], rs[0], rs[1]) != t.maker)
          {
              emit LogError(uint8(Errors.INVALID_SIGNATURE), t.makerOrderHash, t.takerOrderHash);
              return 0;
          }
         
          
          if (ecrecover(keccak256("\x19Ethereum Signed Message:\n32", t.takerOrderHash), v[1], rs[2], rs[3]) != t.taker)
          {
              emit LogError(uint8(Errors.INVALID_SIGNATURE), t.makerOrderHash, t.takerOrderHash);
              return 0;
          }
  
  
  
          
          if ((!takerIsBuying && t.makerPrice < t.takerPrice) || (takerIsBuying && t.takerPrice < t.makerPrice))
          {
              emit LogError(uint8(Errors.INVALID_PRICE), t.makerOrderHash, t.takerOrderHash);
              return 0; 
          }      
  
  
  
           
          
  
          uint256[4] memory balances = EtherMium(exchangeContract).getMakerTakerBalances(t.baseToken, t.maker, t.taker);
  
          
          FuturesTradeValues memory tv = FuturesTradeValues({
              qty                 : 0,
              makerProfit         : 0,
              makerLoss           : 0,
              takerProfit         : 0,
              takerLoss           : 0,
              makerBalance        : balances[0], 
              takerBalance        : balances[1],  
              makerReserve        : balances[2],  
              takerReserve        : balances[3]  
          });
  
  
  
  
           
  
          
          if (futuresContracts[t.futuresContract].floorPrice >= t.makerPrice || futuresContracts[t.futuresContract].capPrice <= t.makerPrice)
          {
              
              emit LogError(uint8(Errors.FLOOR_OR_CAP_PRICE_REACHED), t.makerOrderHash, t.takerOrderHash);
              return 0;
          }
  
          
          
          tv.qty = min(safeSub(t.makerAmount, orderFills[t.makerOrderHash]), safeSub(t.takerAmount, orderFills[t.takerOrderHash]));
          
          if (positionExists(t.makerInversePositionHash) && positionExists(t.takerInversePositionHash))
          {
              tv.qty = min(tv.qty, min(retrievePosition(t.makerInversePositionHash)[0], retrievePosition(t.takerInversePositionHash)[0]));
          }
          else if (positionExists(t.makerInversePositionHash))
          {
              tv.qty = min(tv.qty, retrievePosition(t.makerInversePositionHash)[0]);
          }
          else if (positionExists(t.takerInversePositionHash))
          {
              tv.qty = min(tv.qty, retrievePosition(t.takerInversePositionHash)[0]);
          }
  
         
  
  
  
  
          
          if (tv.qty == 0)
          {
              
              emit LogError(uint8(Errors.ORDER_ALREADY_FILLED), t.makerOrderHash, t.takerOrderHash);
              return 0;
          }
  
          
          if (safeMul(t.takerGasFee, 20) > calculateTradeValue(tv.qty, t.makerPrice, t.futuresContract))
          {
              emit LogError(uint8(Errors.GAS_TOO_HIGH), t.makerOrderHash, t.takerOrderHash);
              return 0;
          } 
  
  
          
          
          
          
          
          
          
  
  
          
  
         
  
          
          if (!takerIsBuying)
          {     
              
        
              
              if (!positionExists(t.makerInversePositionHash) && !positionExists(t.makerPositionHash))
              {
  
  
                  
                  
                  if (!checkEnoughBalance(t.floorPrice, t.makerPrice, tv.qty, true, makerFee, 0, futuresContractHash, safeSub(balances[0],tv.makerReserve)))
                  {
                      
                      emit LogError(uint8(Errors.OUT_OF_BALANCE), t.makerOrderHash, t.takerOrderHash);
                      return 0; 
                  }
  
                  
                  
                  
                  recordNewPosition(t.makerPositionHash, tv.qty, t.makerPrice, 1, block.number);
  
  
  
                  updateBalances(
                      t.futuresContract, 
                      [
                          t.baseToken, 
                          t.maker 
                      ], 
                      t.makerPositionHash,  
                      [
                          tv.qty, 
                          t.makerPrice,  
                          makerFee, 
                          0, 
                          0, 
                          tv.makerBalance, 
                          0, 
                          tv.makerReserve 
                      ], 
                      [
                          true, 
                          true, 
                          false 
                      ]
                  );
  
              } else {               
                  
                  if (positionExists(t.makerPositionHash))
                  {
                      
                      
                      
                      if (!checkEnoughBalance(t.floorPrice, t.makerPrice, tv.qty, true, makerFee, 0, futuresContractHash, safeSub(balances[0],tv.makerReserve)))
                      {
                          
                          emit LogError(uint8(Errors.OUT_OF_BALANCE), t.makerOrderHash, t.takerOrderHash);
                          return 0; 
                      }
  
                      
                      updatePositionSize(t.makerPositionHash, safeAdd(retrievePosition(t.makerPositionHash)[0], tv.qty), t.makerPrice);
                  
                      updateBalances(
                          t.futuresContract, 
                          [
                              t.baseToken,  
                              t.maker 
                          ], 
                          t.makerPositionHash, 
                          [
                              tv.qty, 
                              t.makerPrice, 
                              makerFee, 
                              0, 
                              0, 
                              tv.makerBalance, 
                              0, 
                              tv.makerReserve 
                          ], 
                          [
                              false, 
                              true, 
                              true 
                          ]
                      );
                  }
                  else
                  {
  
                      
                      updatePositionSize(t.makerInversePositionHash, safeSub(retrievePosition(t.makerInversePositionHash)[0], tv.qty), 0);
                      
                      
  
                      if (t.makerPrice < retrievePosition(t.makerInversePositionHash)[1])
                      {
                          
                          
                          tv.makerProfit                      = calculateProfit(t.makerPrice, retrievePosition(t.makerInversePositionHash)[1], tv.qty, futuresContractHash, true);
                      }
                      else
                      {
                          
                          
                          tv.makerLoss                        = calculateLoss(t.makerPrice, retrievePosition(t.makerInversePositionHash)[1], tv.qty, futuresContractHash, true);                                        
                      }
  
  
  
  
                      updateBalances(
                          t.futuresContract, 
                          [
                              t.baseToken, 
                              t.maker 
                          ], 
                          t.makerInversePositionHash, 
                          [
                              tv.qty, 
                              t.makerPrice, 
                              makerFee, 
                              tv.makerProfit,  
                              tv.makerLoss,  
                              tv.makerBalance, 
                              0, 
                              tv.makerReserve 
                          ], 
                          [
                              false, 
                              true, 
                              false 
                          ]
                      );
                  }                
              }
  
             
  
  
              
              if (!positionExists(t.takerInversePositionHash) && !positionExists(t.takerPositionHash))
              {
                  
                  
                  
                  if (!checkEnoughBalance(t.capPrice, t.makerPrice, tv.qty, false, takerFee, t.takerGasFee, futuresContractHash, safeSub(balances[1],tv.takerReserve)))
                  {
                      
                      emit LogError(uint8(Errors.OUT_OF_BALANCE), t.makerOrderHash, t.takerOrderHash);
                      return 0; 
                  }
                  
                  
                  recordNewPosition(t.takerPositionHash, tv.qty, t.makerPrice, 0, block.number);
                  
                  updateBalances(
                      t.futuresContract, 
                      [
                          t.baseToken, 
                          t.taker 
                      ], 
                      t.takerPositionHash, 
                      [
                          tv.qty, 
                          t.makerPrice,  
                          takerFee, 
                          0, 
                          0,  
                          tv.takerBalance,  
                          t.takerGasFee, 
                          tv.takerReserve 
                      ], 
                      [
                          true, 
                          false, 
                          false 
                      ]
                  );
  
              } else {
                  if (positionExists(t.takerPositionHash))
                  {
                      
                      
                      if (!checkEnoughBalance(t.capPrice, t.makerPrice, tv.qty, false, takerFee, t.takerGasFee, futuresContractHash, safeSub(balances[1],tv.takerReserve)))
                      {
                          
                          emit LogError(uint8(Errors.OUT_OF_BALANCE), t.makerOrderHash, t.takerOrderHash);
                          return 0; 
                      }
  
                      
                      updatePositionSize(t.takerPositionHash, safeAdd(retrievePosition(t.takerPositionHash)[0], tv.qty), t.makerPrice);
                  
                      updateBalances(
                          t.futuresContract, 
                          [
                              t.baseToken,  
                              t.taker 
                          ], 
                          t.takerPositionHash, 
                          [
                              tv.qty, 
                              t.makerPrice, 
                              takerFee, 
                              0, 
                              0, 
                              tv.takerBalance, 
                              t.takerGasFee, 
                              tv.takerReserve 
                          ], 
                          [
                              false, 
                              false, 
                              true 
                          ]
                      );
                  }
                  else
                  {   
  
  
                       
                     
  
                      
                      updatePositionSize(t.takerInversePositionHash, safeSub(retrievePosition(t.takerInversePositionHash)[0], tv.qty), 0);
                      
  
  
                      if (t.makerPrice > retrievePosition(t.takerInversePositionHash)[1])
                      {
                          
                          
                          tv.takerProfit                      = calculateProfit(t.makerPrice, retrievePosition(t.takerInversePositionHash)[1], tv.qty, futuresContractHash, false);
                      }
                      else
                      {
                          
                          
                          tv.takerLoss                        = calculateLoss(t.makerPrice, retrievePosition(t.takerInversePositionHash)[1], tv.qty, futuresContractHash, false); 
                      }
  
                    
  
                      updateBalances(
                          t.futuresContract, 
                          [
                              t.baseToken, 
                              t.taker 
                          ], 
                          t.takerInversePositionHash, 
                          [
                              tv.qty, 
                              t.makerPrice, 
                              takerFee, 
                              tv.takerProfit, 
                              tv.takerLoss, 
                              tv.takerBalance,  
                              t.takerGasFee,  
                              tv.takerReserve 
                          ], 
                          [
                              false, 
                              false, 
                              false 
                          ]
                      );
                  }
              }
          }
  
  
          
  
          else
          {      
              
  
              
              if (!positionExists(t.makerInversePositionHash) && !positionExists(t.makerPositionHash))
              {
                  
                  
                  if (!checkEnoughBalance(t.capPrice, t.makerPrice, tv.qty, false, makerFee, 0, futuresContractHash, safeSub(balances[0],tv.makerReserve)))
                  {
                      
                      emit LogError(uint8(Errors.OUT_OF_BALANCE), t.makerOrderHash, t.takerOrderHash);
                      return 0; 
                  }
  
                  
                  recordNewPosition(t.makerPositionHash, tv.qty, t.makerPrice, 0, block.number);
                  updateBalances(
                      t.futuresContract, 
                      [
                          t.baseToken,   
                          t.maker 
                      ], 
                      t.makerPositionHash, 
                      [
                          tv.qty, 
                          t.makerPrice, 
                          makerFee, 
                          0, 
                          0, 
                          tv.makerBalance, 
                          0, 
                          tv.makerReserve 
                      ], 
                      [
                          true, 
                          false, 
                          false 
                      ]
                  );
  
              } else {
                  if (positionExists(t.makerPositionHash))
                  {
                      
                      
                      if (!checkEnoughBalance(t.capPrice, t.makerPrice, tv.qty, false, makerFee, 0, futuresContractHash, safeSub(balances[0],tv.makerReserve)))
                      {
                          
                          emit LogError(uint8(Errors.OUT_OF_BALANCE), t.makerOrderHash, t.takerOrderHash);
                          return 0; 
                      }
  
                      
                      updatePositionSize(t.makerPositionHash, safeAdd(retrievePosition(t.makerPositionHash)[0], tv.qty), t.makerPrice);
                  
                      updateBalances(
                          t.futuresContract, 
                          [
                              t.baseToken,  
                              t.maker 
                          ], 
                          t.makerPositionHash, 
                          [
                              tv.qty, 
                              t.makerPrice, 
                              makerFee, 
                              0, 
                              0, 
                              tv.makerBalance, 
                              0, 
                              tv.makerReserve 
                          ], 
                          [
                              false, 
                              false, 
                              true 
                          ]
                      );
                  }
                  else
                  {
  
                      
                      updatePositionSize(t.makerInversePositionHash, safeSub(retrievePosition(t.makerInversePositionHash)[0], tv.qty), 0);       
                      
  
  
                      if (t.makerPrice > retrievePosition(t.makerInversePositionHash)[1])
                      {
                          
                          
                          tv.makerProfit                      = calculateProfit(t.makerPrice, retrievePosition(t.makerInversePositionHash)[1], tv.qty, futuresContractHash, false);
                      }
                      else
                      {
                          
                          
                          tv.makerLoss                        = calculateLoss(t.makerPrice, retrievePosition(t.makerInversePositionHash)[1], tv.qty, futuresContractHash, false);                               
                      }
  
                     
  
                      updateBalances(
                          t.futuresContract, 
                          [
                              t.baseToken, 
                              t.maker 
                          ], 
                          t.makerInversePositionHash, 
                          [
                              tv.qty, 
                              t.makerPrice, 
                              makerFee, 
                              tv.makerProfit,  
                              tv.makerLoss, 
                              tv.makerBalance, 
                              0, 
                              tv.makerReserve 
                          ], 
                          [
                              false, 
                              false, 
                              false 
                          ]
                      );
                  }
              }
  
              
              if (!positionExists(t.takerInversePositionHash) && !positionExists(t.takerPositionHash))
              {
                  
                  
                  if (!checkEnoughBalance(t.floorPrice, t.makerPrice, tv.qty, true, takerFee, t.takerGasFee, futuresContractHash, safeSub(balances[1],tv.takerReserve)))
                  {
                      
                      emit LogError(uint8(Errors.OUT_OF_BALANCE), t.makerOrderHash, t.takerOrderHash);
                      return 0; 
                  }
  
                  
                  recordNewPosition(t.takerPositionHash, tv.qty, t.makerPrice, 1, block.number);
             
                  updateBalances(
                      t.futuresContract, 
                      [
                          t.baseToken,  
                          t.taker 
                      ], 
                      t.takerPositionHash, 
                      [
                          tv.qty, 
                          t.makerPrice, 
                          takerFee, 
                          0,  
                          0,  
                          tv.takerBalance, 
                          t.takerGasFee, 
                          tv.takerReserve 
                      ], 
                      [
                          true, 
                          true, 
                          false 
                      ]
                  );
  
              } else {
                  if (positionExists(t.takerPositionHash))
                  {
                      
                      
                      if (!checkEnoughBalance(t.floorPrice, t.makerPrice, tv.qty, true, takerFee, t.takerGasFee, futuresContractHash, safeSub(balances[1],tv.takerReserve)))
                      {
                          
                          emit LogError(uint8(Errors.OUT_OF_BALANCE), t.makerOrderHash, t.takerOrderHash);
                          return 0; 
                      }
                      
                      
                      updatePositionSize(t.takerPositionHash, safeAdd(retrievePosition(t.takerPositionHash)[0], tv.qty), t.makerPrice);
                  
                      updateBalances(
                          t.futuresContract, 
                          [
                              t.baseToken,  
                              t.taker 
                          ], 
                          t.takerPositionHash, 
                          [
                              tv.qty, 
                              t.makerPrice, 
                              takerFee, 
                              0, 
                              0, 
                              tv.takerBalance, 
                              t.takerGasFee, 
                              tv.takerReserve 
                          ], 
                          [
                              false, 
                              true, 
                              true 
                          ]
                      );
                  }
                  else
                  {
  
                      
                      updatePositionSize(t.takerInversePositionHash, safeSub(retrievePosition(t.takerInversePositionHash)[0], tv.qty), 0);
                                       
                      if (t.makerPrice < retrievePosition(t.takerInversePositionHash)[1])
                      {
                          
                          
                          tv.takerProfit                      = calculateProfit(t.makerPrice, retrievePosition(t.takerInversePositionHash)[1], tv.qty, futuresContractHash, true);
                      }
                      else
                      {
                          
                          
                          tv.takerLoss                        = calculateLoss(t.makerPrice, retrievePosition(t.takerInversePositionHash)[1], tv.qty, futuresContractHash, true);                                  
                      }
  
                      
  
                      updateBalances(
                          t.futuresContract, 
                          [
                              t.baseToken,   
                              t.taker 
                          ], 
                          t.takerInversePositionHash,  
                          [
                              tv.qty, 
                              t.makerPrice, 
                              takerFee, 
                              tv.takerProfit, 
                              tv.takerLoss, 
                              tv.takerBalance, 
                              t.takerGasFee, 
                              tv.takerReserve 
                          ], 
                          [
                              false, 
                              true, 
                              false 
                          ]
                      );
                  }
              }           
          }
  
  
          orderFills[t.makerOrderHash]            = safeAdd(orderFills[t.makerOrderHash], tv.qty); 
          orderFills[t.takerOrderHash]            = safeAdd(orderFills[t.takerOrderHash], tv.qty); 
  
  
          emit FuturesTrade(takerIsBuying, tv.qty, t.makerPrice, t.futuresContract, t.makerOrderHash, t.takerOrderHash);
  
          return tv.qty;
      }