contract LibAbiEncoder {

    /// @dev ABI encodes calldata for `fillOrder`.
    /// @param order Order struct containing order specifications.
    /// @param takerAssetFillAmount Desired amount of takerAsset to sell.
    /// @param signature Proof that order has been created by maker.
    /// @return ABI encoded calldata for `fillOrder`.
    function abiEncodeFillOrder(
        LibOrder.Order memory order,
        uint256 takerAssetFillAmount,
        bytes memory signature
    )
        internal
        pure
        returns (bytes memory fillOrderCalldata)
    {
        // We need to call MExchangeCore.fillOrder using a delegatecall in
        // assembly so that we can intercept a call that throws. For this, we
        // need the input encoded in memory in the Ethereum ABIv2 format [1].

        // | Area     | Offset | Length  | Contents                                    |
        // | -------- |--------|---------|-------------------------------------------- |
        // | Header   | 0x00   | 4       | function selector                           |
        // | Params   |        | 3 * 32  | function parameters:                        |
        // |          | 0x00   |         |   1. offset to order (*)                    |
        // |          | 0x20   |         |   2. takerAssetFillAmount                   |
        // |          | 0x40   |         |   3. offset to signature (*)                |
        // | Data     |        | 12 * 32 | order:                                      |
        // |          | 0x000  |         |   1.  senderAddress                         |
        // |          | 0x020  |         |   2.  makerAddress                          |
        // |          | 0x040  |         |   3.  takerAddress                          |
        // |          | 0x060  |         |   4.  feeRecipientAddress                   |
        // |          | 0x080  |         |   5.  makerAssetAmount                      |
        // |          | 0x0A0  |         |   6.  takerAssetAmount                      |
        // |          | 0x0C0  |         |   7.  makerFeeAmount                        |
        // |          | 0x0E0  |         |   8.  takerFeeAmount                        |
        // |          | 0x100  |         |   9.  expirationTimeSeconds                 |
        // |          | 0x120  |         |   10. salt                                  |
        // |          | 0x140  |         |   11. Offset to makerAssetData (*)          |
        // |          | 0x160  |         |   12. Offset to takerAssetData (*)          |
        // |          | 0x180  | 32      | makerAssetData Length                       |
        // |          | 0x1A0  | **      | makerAssetData Contents                     |
        // |          | 0x1C0  | 32      | takerAssetData Length                       |
        // |          | 0x1E0  | **      | takerAssetData Contents                     |
        // |          | 0x200  | 32      | signature Length                            |
        // |          | 0x220  | **      | signature Contents                          |

        // * Offsets are calculated from the beginning of the current area: Header, Params, Data:
        //     An offset stored in the Params area is calculated from the beginning of the Params section.
        //     An offset stored in the Data area is calculated from the beginning of the Data section.

        // ** The length of dynamic array contents are stored in the field immediately preceeding the contents.

        // [1]: https://solidity.readthedocs.io/en/develop/abi-spec.html

        assembly {

            // Areas below may use the following variables:
            //   1. <area>Start   -- Start of this area in memory
            //   2. <area>End     -- End of this area in memory. This value may
            //                       be precomputed (before writing contents),
            //                       or it may be computed as contents are written.
            //   3. <area>Offset  -- Current offset into area. If an area's End
            //                       is precomputed, this variable tracks the
            //                       offsets of contents as they are written.

            /////// Setup Header Area ///////
            // Load free memory pointer
            fillOrderCalldata := mload(0x40)
            // bytes4(keccak256("fillOrder((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes),uint256,bytes)"))
            // = 0xb4be83d5
            // Leave 0x20 bytes to store the length
            mstore(add(fillOrderCalldata, 0x20), 0xb4be83d500000000000000000000000000000000000000000000000000000000)
            let headerAreaEnd := add(fillOrderCalldata, 0x24)

            /////// Setup Params Area ///////
            // This area is preallocated and written to later.
            // This is because we need to fill in offsets that have not yet been calculated.
            let paramsAreaStart := headerAreaEnd
            let paramsAreaEnd := add(paramsAreaStart, 0x60)
            let paramsAreaOffset := paramsAreaStart

            /////// Setup Data Area ///////
            let dataAreaStart := paramsAreaEnd
            let dataAreaEnd := dataAreaStart

            // Offset from the source data we're reading from
            let sourceOffset := order
            // arrayLenBytes and arrayLenWords track the length of a dynamically-allocated bytes array.
            let arrayLenBytes := 0
            let arrayLenWords := 0

            /////// Write order Struct ///////
            // Write memory location of Order, relative to the start of the
            // parameter list, then increment the paramsAreaOffset respectively.
            mstore(paramsAreaOffset, sub(dataAreaEnd, paramsAreaStart))
            paramsAreaOffset := add(paramsAreaOffset, 0x20)

            // Write values for each field in the order
            // It would be nice to use a loop, but we save on gas by writing
            // the stores sequentially.
            mstore(dataAreaEnd, mload(sourceOffset))                            // makerAddress
            mstore(add(dataAreaEnd, 0x20), mload(add(sourceOffset, 0x20)))      // takerAddress
            mstore(add(dataAreaEnd, 0x40), mload(add(sourceOffset, 0x40)))      // feeRecipientAddress
            mstore(add(dataAreaEnd, 0x60), mload(add(sourceOffset, 0x60)))      // senderAddress
            mstore(add(dataAreaEnd, 0x80), mload(add(sourceOffset, 0x80)))      // makerAssetAmount
            mstore(add(dataAreaEnd, 0xA0), mload(add(sourceOffset, 0xA0)))      // takerAssetAmount
            mstore(add(dataAreaEnd, 0xC0), mload(add(sourceOffset, 0xC0)))      // makerFeeAmount
            mstore(add(dataAreaEnd, 0xE0), mload(add(sourceOffset, 0xE0)))      // takerFeeAmount
            mstore(add(dataAreaEnd, 0x100), mload(add(sourceOffset, 0x100)))    // expirationTimeSeconds
            mstore(add(dataAreaEnd, 0x120), mload(add(sourceOffset, 0x120)))    // salt
            mstore(add(dataAreaEnd, 0x140), mload(add(sourceOffset, 0x140)))    // Offset to makerAssetData
            mstore(add(dataAreaEnd, 0x160), mload(add(sourceOffset, 0x160)))    // Offset to takerAssetData
            dataAreaEnd := add(dataAreaEnd, 0x180)
            sourceOffset := add(sourceOffset, 0x180)

            // Write offset to <order.makerAssetData>
            mstore(add(dataAreaStart, mul(10, 0x20)), sub(dataAreaEnd, dataAreaStart))

            // Calculate length of <order.makerAssetData>
            sourceOffset := mload(add(order, 0x140)) // makerAssetData
            arrayLenBytes := mload(sourceOffset)
            sourceOffset := add(sourceOffset, 0x20)
            arrayLenWords := div(add(arrayLenBytes, 0x1F), 0x20)

            // Write length of <order.makerAssetData>
            mstore(dataAreaEnd, arrayLenBytes)
            dataAreaEnd := add(dataAreaEnd, 0x20)

            // Write contents of <order.makerAssetData>
            for {let i := 0} lt(i, arrayLenWords) {i := add(i, 1)} {
                mstore(dataAreaEnd, mload(sourceOffset))
                dataAreaEnd := add(dataAreaEnd, 0x20)
                sourceOffset := add(sourceOffset, 0x20)
            }

            // Write offset to <order.takerAssetData>
            mstore(add(dataAreaStart, mul(11, 0x20)), sub(dataAreaEnd, dataAreaStart))

            // Calculate length of <order.takerAssetData>
            sourceOffset := mload(add(order, 0x160)) // takerAssetData
            arrayLenBytes := mload(sourceOffset)
            sourceOffset := add(sourceOffset, 0x20)
            arrayLenWords := div(add(arrayLenBytes, 0x1F), 0x20)

            // Write length of <order.takerAssetData>
            mstore(dataAreaEnd, arrayLenBytes)
            dataAreaEnd := add(dataAreaEnd, 0x20)

            // Write contents of  <order.takerAssetData>
            for {let i := 0} lt(i, arrayLenWords) {i := add(i, 1)} {
                mstore(dataAreaEnd, mload(sourceOffset))
                dataAreaEnd := add(dataAreaEnd, 0x20)
                sourceOffset := add(sourceOffset, 0x20)
            }

            /////// Write takerAssetFillAmount ///////
            mstore(paramsAreaOffset, takerAssetFillAmount)
            paramsAreaOffset := add(paramsAreaOffset, 0x20)

            /////// Write signature ///////
            // Write offset to paramsArea
            mstore(paramsAreaOffset, sub(dataAreaEnd, paramsAreaStart))

            // Calculate length of signature
            sourceOffset := signature
            arrayLenBytes := mload(sourceOffset)
            sourceOffset := add(sourceOffset, 0x20)
            arrayLenWords := div(add(arrayLenBytes, 0x1F), 0x20)

            // Write length of signature
            mstore(dataAreaEnd, arrayLenBytes)
            dataAreaEnd := add(dataAreaEnd, 0x20)

            // Write contents of signature
            for {let i := 0} lt(i, arrayLenWords) {i := add(i, 1)} {
                mstore(dataAreaEnd, mload(sourceOffset))
                dataAreaEnd := add(dataAreaEnd, 0x20)
                sourceOffset := add(sourceOffset, 0x20)
            }

            // Set length of calldata
            mstore(fillOrderCalldata, sub(dataAreaEnd, add(fillOrderCalldata, 0x20)))

            // Increment free memory pointer
            mstore(0x40, dataAreaEnd)
        }

        return fillOrderCalldata;
    }
}
function abiEncodeFillOrder(
    LibOrder.Order memory order,
    uint256 takerAssetFillAmount,
    bytes memory signature
)
    internal
    pure
    returns (bytes memory fillOrderCalldata)
{
    // We need to call MExchangeCore.fillOrder using a delegatecall in
    // assembly so that we can intercept a call that throws. For this, we
    // need the input encoded in memory in the Ethereum ABIv2 format [1].

    // | Area     | Offset | Length  | Contents                                    |
    // | -------- |--------|---------|-------------------------------------------- |
    // | Header   | 0x00   | 4       | function selector                           |
    // | Params   |        | 3 * 32  | function parameters:                        |
    // |          | 0x00   |         |   1. offset to order (*)                    |
    // |          | 0x20   |         |   2. takerAssetFillAmount                   |
    // |          | 0x40   |         |   3. offset to signature (*)                |
    // | Data     |        | 12 * 32 | order:                                      |
    // |          | 0x000  |         |   1.  senderAddress                         |
    // |          | 0x020  |         |   2.  makerAddress                          |
    // |          | 0x040  |         |   3.  takerAddress                          |
    // |          | 0x060  |         |   4.  feeRecipientAddress                   |
    // |          | 0x080  |         |   5.  makerAssetAmount                      |
    // |          | 0x0A0  |         |   6.  takerAssetAmount                      |
    // |          | 0x0C0  |         |   7.  makerFeeAmount                        |
    // |          | 0x0E0  |         |   8.  takerFeeAmount                        |
    // |          | 0x100  |         |   9.  expirationTimeSeconds                 |
    // |          | 0x120  |         |   10. salt                                  |
    // |          | 0x140  |         |   11. Offset to makerAssetData (*)          |
    // |          | 0x160  |         |   12. Offset to takerAssetData (*)          |
    // |          | 0x180  | 32      | makerAssetData Length                       |
    // |          | 0x1A0  | **      | makerAssetData Contents                     |
    // |          | 0x1C0  | 32      | takerAssetData Length                       |
    // |          | 0x1E0  | **      | takerAssetData Contents                     |
    // |          | 0x200  | 32      | signature Length                            |
    // |          | 0x220  | **      | signature Contents                          |

    // * Offsets are calculated from the beginning of the current area: Header, Params, Data:
    //     An offset stored in the Params area is calculated from the beginning of the Params section.
    //     An offset stored in the Data area is calculated from the beginning of the Data section.

    // ** The length of dynamic array contents are stored in the field immediately preceeding the contents.

    // [1]: https://solidity.readthedocs.io/en/develop/abi-spec.html

    assembly {

        // Areas below may use the following variables:
        //   1. <area>Start   -- Start of this area in memory
        //   2. <area>End     -- End of this area in memory. This value may
        //                       be precomputed (before writing contents),
        //                       or it may be computed as contents are written.
        //   3. <area>Offset  -- Current offset into area. If an area's End
        //                       is precomputed, this variable tracks the
        //                       offsets of contents as they are written.

        /////// Setup Header Area ///////
        // Load free memory pointer
        fillOrderCalldata := mload(0x40)
        // bytes4(keccak256("fillOrder((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes),uint256,bytes)"))
        // = 0xb4be83d5
        // Leave 0x20 bytes to store the length
        mstore(add(fillOrderCalldata, 0x20), 0xb4be83d500000000000000000000000000000000000000000000000000000000)
        let headerAreaEnd := add(fillOrderCalldata, 0x24)

        /////// Setup Params Area ///////
        // This area is preallocated and written to later.
        // This is because we need to fill in offsets that have not yet been calculated.
        let paramsAreaStart := headerAreaEnd
        let paramsAreaEnd := add(paramsAreaStart, 0x60)
        let paramsAreaOffset := paramsAreaStart

        /////// Setup Data Area ///////
        let dataAreaStart := paramsAreaEnd
        let dataAreaEnd := dataAreaStart

        // Offset from the source data we're reading from
        let sourceOffset := order
        // arrayLenBytes and arrayLenWords track the length of a dynamically-allocated bytes array.
        let arrayLenBytes := 0
        let arrayLenWords := 0

        /////// Write order Struct ///////
        // Write memory location of Order, relative to the start of the
        // parameter list, then increment the paramsAreaOffset respectively.
        mstore(paramsAreaOffset, sub(dataAreaEnd, paramsAreaStart))
        paramsAreaOffset := add(paramsAreaOffset, 0x20)

        // Write values for each field in the order
        // It would be nice to use a loop, but we save on gas by writing
        // the stores sequentially.
        mstore(dataAreaEnd, mload(sourceOffset))                            // makerAddress
        mstore(add(dataAreaEnd, 0x20), mload(add(sourceOffset, 0x20)))      // takerAddress
        mstore(add(dataAreaEnd, 0x40), mload(add(sourceOffset, 0x40)))      // feeRecipientAddress
        mstore(add(dataAreaEnd, 0x60), mload(add(sourceOffset, 0x60)))      // senderAddress
        mstore(add(dataAreaEnd, 0x80), mload(add(sourceOffset, 0x80)))      // makerAssetAmount
        mstore(add(dataAreaEnd, 0xA0), mload(add(sourceOffset, 0xA0)))      // takerAssetAmount
        mstore(add(dataAreaEnd, 0xC0), mload(add(sourceOffset, 0xC0)))      // makerFeeAmount
        mstore(add(dataAreaEnd, 0xE0), mload(add(sourceOffset, 0xE0)))      // takerFeeAmount
        mstore(add(dataAreaEnd, 0x100), mload(add(sourceOffset, 0x100)))    // expirationTimeSeconds
        mstore(add(dataAreaEnd, 0x120), mload(add(sourceOffset, 0x120)))    // salt
        mstore(add(dataAreaEnd, 0x140), mload(add(sourceOffset, 0x140)))    // Offset to makerAssetData
        mstore(add(dataAreaEnd, 0x160), mload(add(sourceOffset, 0x160)))    // Offset to takerAssetData
        dataAreaEnd := add(dataAreaEnd, 0x180)
        sourceOffset := add(sourceOffset, 0x180)

        // Write offset to <order.makerAssetData>
        mstore(add(dataAreaStart, mul(10, 0x20)), sub(dataAreaEnd, dataAreaStart))

        // Calculate length of <order.makerAssetData>
        sourceOffset := mload(add(order, 0x140)) // makerAssetData
        arrayLenBytes := mload(sourceOffset)
        sourceOffset := add(sourceOffset, 0x20)
        arrayLenWords := div(add(arrayLenBytes, 0x1F), 0x20)

        // Write length of <order.makerAssetData>
        mstore(dataAreaEnd, arrayLenBytes)
        dataAreaEnd := add(dataAreaEnd, 0x20)

        // Write contents of <order.makerAssetData>
        for {let i := 0} lt(i, arrayLenWords) {i := add(i, 1)} {
            mstore(dataAreaEnd, mload(sourceOffset))
            dataAreaEnd := add(dataAreaEnd, 0x20)
            sourceOffset := add(sourceOffset, 0x20)
        }

        // Write offset to <order.takerAssetData>
        mstore(add(dataAreaStart, mul(11, 0x20)), sub(dataAreaEnd, dataAreaStart))

        // Calculate length of <order.takerAssetData>
        sourceOffset := mload(add(order, 0x160)) // takerAssetData
        arrayLenBytes := mload(sourceOffset)
        sourceOffset := add(sourceOffset, 0x20)
        arrayLenWords := div(add(arrayLenBytes, 0x1F), 0x20)

        // Write length of <order.takerAssetData>
        mstore(dataAreaEnd, arrayLenBytes)
        dataAreaEnd := add(dataAreaEnd, 0x20)

        // Write contents of  <order.takerAssetData>
        for {let i := 0} lt(i, arrayLenWords) {i := add(i, 1)} {
            mstore(dataAreaEnd, mload(sourceOffset))
            dataAreaEnd := add(dataAreaEnd, 0x20)
            sourceOffset := add(sourceOffset, 0x20)
        }

        /////// Write takerAssetFillAmount ///////
        mstore(paramsAreaOffset, takerAssetFillAmount)
        paramsAreaOffset := add(paramsAreaOffset, 0x20)

        /////// Write signature ///////
        // Write offset to paramsArea
        mstore(paramsAreaOffset, sub(dataAreaEnd, paramsAreaStart))

        // Calculate length of signature
        sourceOffset := signature
        arrayLenBytes := mload(sourceOffset)
        sourceOffset := add(sourceOffset, 0x20)
        arrayLenWords := div(add(arrayLenBytes, 0x1F), 0x20)

        // Write length of signature
        mstore(dataAreaEnd, arrayLenBytes)
        dataAreaEnd := add(dataAreaEnd, 0x20)

        // Write contents of signature
        for {let i := 0} lt(i, arrayLenWords) {i := add(i, 1)} {
            mstore(dataAreaEnd, mload(sourceOffset))
            dataAreaEnd := add(dataAreaEnd, 0x20)
            sourceOffset := add(sourceOffset, 0x20)
        }

        // Set length of calldata
        mstore(fillOrderCalldata, sub(dataAreaEnd, add(fillOrderCalldata, 0x20)))

        // Increment free memory pointer
        mstore(0x40, dataAreaEnd)
    }

    return fillOrderCalldata;
}

/// @dev ABI encodes calldata for `fillOrder`.
/// @param order Order struct containing order specifications.
/// @param takerAssetFillAmount Desired amount of takerAsset to sell.
/// @param signature Proof that order has been created by maker.
/// @return ABI encoded calldata for `fillOrder`.