contract dexBlueStructs is dexBlueStorage{

    // EIP712 Domain
    struct EIP712_Domain {
        string  name;
        string  version;
        uint256 chainId;
        address verifyingContract;
    }
    bytes32 constant EIP712_DOMAIN_TYPEHASH = keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
    bytes32          EIP712_DOMAIN_SEPARATOR;
    // Order typehash
    bytes32 constant EIP712_ORDER_TYPEHASH = keccak256("Order(address sellTokenAddress,uint128 sellTokenAmount,address buyTokenAddress,uint128 buyTokenAmount,uint32 expiry,uint64 nonce)");
    // Withdrawal typehash
    bytes32 constant EIP712_WITHDRAWAL_TYPEHASH = keccak256("Withdrawal(address token,uint256 amount,uint64 nonce)");

    
    struct Order{
        address     sellToken;     // The token, the order signee wants to sell
        uint256     sellAmount;    // The total amount the signee wants to give for the amount he wants to buy (the orders "rate" is implied by the ratio between the two amounts)
        address     buyToken;      // The token, the order signee wants to buy
        uint256     buyAmount;     // The total amount the signee wants to buy
        uint256     expiry;        // The expiry time of the order (after which it is not longer valid)
        bytes32     hash;          // The orders hash
        address     signee;        // The orders signee
    }

    struct OrderInputPacked{
        /*
            BITMASK                                                            | BYTE RANGE | DESCRIPTION
            -------------------------------------------------------------------|------------|----------------------------------
            0xffffffffffffffffffffffffffffffff00000000000000000000000000000000 |  0 - 15    | sell amount
            0x00000000000000000000000000000000ffffffffffffffffffffffffffffffff | 16 - 31    | buy amount
        */  
        bytes32     packedInput1;
        /*
            BITMASK                                                            | BYTE RANGE | DESCRIPTION
            -------------------------------------------------------------------|------------|----------------------------------
            0xffff000000000000000000000000000000000000000000000000000000000000 |  0 -  1    | sell token identifier
            0x0000ffff00000000000000000000000000000000000000000000000000000000 |  2 -  3    | buy token identifier
            0x00000000ffffffff000000000000000000000000000000000000000000000000 |  4 -  7    | expiry
            0x0000000000000000ffffffffffffffff00000000000000000000000000000000 |  8 - 15    | nonce
            0x00000000000000000000000000000000ff000000000000000000000000000000 | 16 - 16    | v
            0x0000000000000000000000000000000000ff0000000000000000000000000000 | 17 - 17    | signing scheme 0x00 = personal.sign, 0x01 = EIP712
            0x000000000000000000000000000000000000ff00000000000000000000000000 | 18 - 18    | signed by delegate
        */
        bytes32     packedInput2;
        
        bytes32     r;                          // Signature r
        bytes32     s;                          // Signature s
    }
    
    /** @notice Helper function parse an Order struct from an OrderInputPacked struct
      * @param   orderInput  The OrderInputPacked struct to parse
      * @return The parsed Order struct
      */
    function orderFromInput(OrderInputPacked memory orderInput) view public returns(Order memory){
        // Parse packed input
        Order memory order = Order({
            sellToken  : tokens[uint256(orderInput.packedInput2 >> 240)],
            sellAmount : uint256(orderInput.packedInput1 >> 128),
            buyToken   : tokens[uint256((orderInput.packedInput2 & 0x0000ffff00000000000000000000000000000000000000000000000000000000) >> 224)],
            buyAmount  : uint256(orderInput.packedInput1 & 0x00000000000000000000000000000000ffffffffffffffffffffffffffffffff),
            expiry     : uint256((orderInput.packedInput2 & 0x00000000ffffffff000000000000000000000000000000000000000000000000) >> 192), 
            hash       : 0x0,
            signee     : address(0x0)
        });
        
        // Restore order hash
        if(
            orderInput.packedInput2[17] == byte(0x00)   // Signing scheme
        ){                                              // Order is hashed after signature scheme personal.sign()
            order.hash = keccak256(abi.encodePacked(    // Restore the hash of this order
                "\x19Ethereum Signed Message:\n32",
                keccak256(abi.encodePacked(
                    order.sellToken,
                    uint128(order.sellAmount),
                    order.buyToken,
                    uint128(order.buyAmount),
                    uint32(order.expiry), 
                    uint64(uint256((orderInput.packedInput2 & 0x0000000000000000ffffffffffffffff00000000000000000000000000000000) >> 128)), // nonce     
                    address(this)                       // This contract's address
                ))
            ));
        }else{                                          // Order is hashed after EIP712
            order.hash = keccak256(abi.encodePacked(
                "\x19\x01",
                EIP712_DOMAIN_SEPARATOR,
                keccak256(abi.encode(
                    EIP712_ORDER_TYPEHASH,
                    order.sellToken,
                    order.sellAmount,
                    order.buyToken,
                    order.buyAmount,
                    order.expiry, 
                    uint256((orderInput.packedInput2 & 0x0000000000000000ffffffffffffffff00000000000000000000000000000000) >> 128) // nonce   
                ))
            ));
        }
        
        // Restore the signee of this order
        order.signee = ecrecover(
            order.hash,                             // Order hash
            uint8(orderInput.packedInput2[16]),     // Signature v
            orderInput.r,                           // Signature r
            orderInput.s                            // Signature s
        );
        
        // When the signature was delegated restore delegating address
        if(
            orderInput.packedInput2[18] == byte(0x01)  // Is delegated
        ){
            order.signee = delegates[order.signee];
        }
        
        return order;
    }
    
    struct Trade{
        uint256 makerAmount;
        uint256 takerAmount; 
        uint256 makerFee; 
        uint256 takerFee;
        uint256 makerRebate;
    }
    
    struct ReserveReserveTrade{
        address makerToken;
        address takerToken; 
        uint256 makerAmount;
        uint256 takerAmount; 
        uint256 makerFee; 
        uint256 takerFee;
        uint256 gasLimit;
    }
    
    struct ReserveTrade{
        uint256 orderAmount;
        uint256 reserveAmount; 
        uint256 orderFee; 
        uint256 reserveFee;
        uint256 orderRebate;
        uint256 reserveRebate;
        bool    orderIsMaker;
        uint256 gasLimit;
    }
    
    struct TradeInputPacked{
        /* 
            BITMASK                                                            | BYTE RANGE | DESCRIPTION
            -------------------------------------------------------------------|------------|----------------------------------
            0xffffffffffffffffffffffffffffffff00000000000000000000000000000000 |  0 - 15    | maker amount
            0x00000000000000000000000000000000ffffffffffffffffffffffffffffffff | 16 - 31    | taker amount
        */
        bytes32     packedInput1;  
        /*
            BITMASK                                                            | BYTE RANGE | DESCRIPTION
            -------------------------------------------------------------------|------------|----------------------------------
            0xffffffffffffffffffffffffffffffff00000000000000000000000000000000 |  0-15      | maker fee
            0x00000000000000000000000000000000ffffffffffffffffffffffffffffffff | 16-31      | taker fee
        */
        bytes32     packedInput2; 
        /*
            BITMASK                                                            | BYTE RANGE | DESCRIPTION
            -------------------------------------------------------------------|------------|----------------------------------
            0xffffffffffffffffffffffffffffffff00000000000000000000000000000000 |  0 - 15    | maker rebate           (optional)
            0x00000000000000000000000000000000ff000000000000000000000000000000 | 16 - 16    | counterparty types:
                                                                               |            |   0x11 : maker=order,   taker=order, 
                                                                               |            |   0x10 : maker=order,   taker=reserve, 
                                                                               |            |   0x01 : maker=reserve, taker=order
                                                                               |            |   0x00 : maker=reserve, taker=reserve
            0x0000000000000000000000000000000000ffff00000000000000000000000000 | 17 - 18    | maker_identifier
            0x00000000000000000000000000000000000000ffff0000000000000000000000 | 19 - 20    | taker_identifier
            0x000000000000000000000000000000000000000000ffff000000000000000000 | 21 - 22    | maker_token_identifier (optional)
            0x0000000000000000000000000000000000000000000000ffff00000000000000 | 23 - 24    | taker_token_identifier (optional)
            0x00000000000000000000000000000000000000000000000000ffffff00000000 | 25 - 27    | gas_limit              (optional)
            0x00000000000000000000000000000000000000000000000000000000ff000000 | 28 - 28    | burn_gas_tokens        (optional)
        */
        bytes32     packedInput3; 
    }

    /** @notice Helper function parse an Trade struct from an TradeInputPacked struct
      * @param  packed      The TradeInputPacked struct to parse
      * @return The parsed Trade struct
      */
    function tradeFromInput(TradeInputPacked memory packed) public pure returns (Trade memory){
        return Trade({
            makerAmount : uint256(packed.packedInput1 >> 128),
            takerAmount : uint256(packed.packedInput1 & 0x00000000000000000000000000000000ffffffffffffffffffffffffffffffff),
            makerFee    : uint256(packed.packedInput2 >> 128),
            takerFee    : uint256(packed.packedInput2 & 0x00000000000000000000000000000000ffffffffffffffffffffffffffffffff),
            makerRebate : uint256(packed.packedInput3 >> 128)
        });
    }
    
    /** @notice Helper function parse an ReserveTrade struct from an TradeInputPacked struct
      * @param  packed      The TradeInputPacked struct to parse
      * @return The parsed ReserveTrade struct
      */
    function reserveTradeFromInput(TradeInputPacked memory packed) public pure returns (ReserveTrade memory){
        if(packed.packedInput3[16] == byte(0x10)){
            // maker is order, taker is reserve
            return ReserveTrade({
                orderAmount   : uint256( packed.packedInput1 >> 128),
                reserveAmount : uint256( packed.packedInput1 & 0x00000000000000000000000000000000ffffffffffffffffffffffffffffffff),
                orderFee      : uint256( packed.packedInput2 >> 128),
                reserveFee    : uint256( packed.packedInput2 & 0x00000000000000000000000000000000ffffffffffffffffffffffffffffffff),
                orderRebate   : uint256((packed.packedInput3 & 0xffffffffffffffffffffffffffffffff00000000000000000000000000000000) >> 128),
                reserveRebate : 0,
                orderIsMaker  : true,
                gasLimit      : uint256((packed.packedInput3 & 0x00000000000000000000000000000000000000000000000000ffffff00000000) >> 32)
            });
        }else{
            // taker is order, maker is reserve
            return ReserveTrade({
                orderAmount   : uint256( packed.packedInput1 & 0x00000000000000000000000000000000ffffffffffffffffffffffffffffffff),
                reserveAmount : uint256( packed.packedInput1 >> 128),
                orderFee      : uint256( packed.packedInput2 & 0x00000000000000000000000000000000ffffffffffffffffffffffffffffffff),
                reserveFee    : uint256( packed.packedInput2 >> 128),
                orderRebate   : 0,
                reserveRebate : uint256((packed.packedInput3 & 0xffffffffffffffffffffffffffffffff00000000000000000000000000000000) >> 128),
                orderIsMaker  : false,
                gasLimit      : uint256((packed.packedInput3 & 0x00000000000000000000000000000000000000000000000000ffffff00000000) >> 32)
            });
        }
    }

    /** @notice Helper function parse an ReserveReserveTrade struct from an TradeInputPacked struct
      * @param  packed      The TradeInputPacked struct to parse
      * @return The parsed ReserveReserveTrade struct
      */
    function reserveReserveTradeFromInput(TradeInputPacked memory packed) public view returns (ReserveReserveTrade memory){
        return ReserveReserveTrade({
            makerToken    : tokens[uint256((packed.packedInput3 & 0x000000000000000000000000000000000000000000ffff000000000000000000) >> 72)],
            takerToken    : tokens[uint256((packed.packedInput3 & 0x0000000000000000000000000000000000000000000000ffff00000000000000) >> 56)],
            makerAmount   : uint256( packed.packedInput1 >> 128),
            takerAmount   : uint256( packed.packedInput1 & 0x00000000000000000000000000000000ffffffffffffffffffffffffffffffff),
            makerFee      : uint256( packed.packedInput2 >> 128),
            takerFee      : uint256( packed.packedInput2 & 0x00000000000000000000000000000000ffffffffffffffffffffffffffffffff),
            gasLimit      : uint256((packed.packedInput3 & 0x00000000000000000000000000000000000000000000000000ffffff00000000) >> 32)
        });
    }
    
    struct RingTrade {
        bool    isReserve;      // 1 if this trade is from a reserve, 0 when from an order
        uint256 identifier;     // identifier of the reserve or order
        address giveToken;      // the token this trade gives, the receive token is the givetoken of the previous ring element
        uint256 giveAmount;     // the amount of giveToken, this ring element is giving for the amount it reeives from the previous element
        uint256 fee;            // the fee this ring element has to pay on the giveToken giveAmount of the previous ring element
        uint256 rebate;         // the rebate on giveAmount this element receives
        uint256 gasLimit;       // the gas limit for the reserve call (if the element is a reserve)
    }

    struct RingTradeInputPacked{
        /* 
            BITMASK                                                            | BYTE RANGE | DESCRIPTION
            -------------------------------------------------------------------|------------|----------------------------------
            0xffffffffffffffffffffffffffffffff00000000000000000000000000000000 |  0 - 15    | give amount
            0x00000000000000000000000000000000ffffffffffffffffffffffffffffffff | 16 - 31    | fee
        */
        bytes32     packedInput1;    
        /* 
            BITMASK                                                            | BYTE RANGE | DESCRIPTION
            -------------------------------------------------------------------|------------|----------------------------------
            0xffffffffffffffffffffffffffffffff00000000000000000000000000000000 |  0 - 15    | rebate
            0x00000000000000000000000000000000ff000000000000000000000000000000 | 16 - 16    | is reserve
            0x0000000000000000000000000000000000ffff00000000000000000000000000 | 17 - 18    | identifier
            0x00000000000000000000000000000000000000ffff0000000000000000000000 | 19 - 20    | giveToken identifier
            0x000000000000000000000000000000000000000000ffffff0000000000000000 | 21 - 23    | gas_limit
            0x000000000000000000000000000000000000000000000000ff00000000000000 | 24 - 24    | burn_gas_tokens
        */
        bytes32     packedInput2;   
    }
    
    /** @notice Helper function parse an RingTrade struct from an RingTradeInputPacked struct
      * @param  packed  The RingTradeInputPacked struct to parse
      * @return The parsed RingTrade struct
      */
    function ringTradeFromInput(RingTradeInputPacked memory packed) view public returns(RingTrade memory){
        return RingTrade({
            isReserve     : (packed.packedInput2[16] == bytes1(0x01)),
            identifier    : uint256((       packed.packedInput2 & 0x0000000000000000000000000000000000ffff00000000000000000000000000) >> 104),
            giveToken     : tokens[uint256((packed.packedInput2 & 0x00000000000000000000000000000000000000ffff0000000000000000000000) >> 88)],
            giveAmount    : uint256(        packed.packedInput1                                                                       >> 128),
            fee           : uint256(        packed.packedInput1 & 0x00000000000000000000000000000000ffffffffffffffffffffffffffffffff),
            rebate        : uint256(        packed.packedInput2                                                                       >> 128),
            gasLimit      : uint256((       packed.packedInput2 & 0x000000000000000000000000000000000000000000ffffff0000000000000000) >> 64)
        });
    }
}
function tradeFromInput(TradeInputPacked memory packed) public pure returns (Trade memory){
    return Trade({
        makerAmount : uint256(packed.packedInput1 >> 128),
        takerAmount : uint256(packed.packedInput1 & 0x00000000000000000000000000000000ffffffffffffffffffffffffffffffff),
        makerFee    : uint256(packed.packedInput2 >> 128),
        takerFee    : uint256(packed.packedInput2 & 0x00000000000000000000000000000000ffffffffffffffffffffffffffffffff),
        makerRebate : uint256(packed.packedInput3 >> 128)
    });
}

/** @notice Helper function parse an Trade struct from an TradeInputPacked struct
 * @param  packed      The TradeInputPacked struct to parse
 * @return The parsed Trade struct
 */