From 242116da93bac72c89305de1a88d9842bb966302 Mon Sep 17 00:00:00 2001 From: felix2feng Date: Fri, 29 Jun 2018 01:02:18 -0700 Subject: [PATCH 1/9] Start exchangewrapper --- .../core/external/ZeroExExchangeWrapper.sol | 107 ++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 contracts/core/external/ZeroExExchangeWrapper.sol diff --git a/contracts/core/external/ZeroExExchangeWrapper.sol b/contracts/core/external/ZeroExExchangeWrapper.sol new file mode 100644 index 000000000..14c256302 --- /dev/null +++ b/contracts/core/external/ZeroExExchangeWrapper.sol @@ -0,0 +1,107 @@ +/* + Copyright 2018 Set Labs Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +pragma solidity 0.4.24; + + + +/** + * @title ZeroExExchangeWrapper + * @author Set Protocol + * + * The ZeroExExchangeWrapper contract wrapper to interface with 0x V2 + */ +contract ZeroExExchangeWrapper +{ + using SafeMath for uint256; + + /* ============ Constants ============ */ + + + + /* ============ Structs ============ */ + struct Order { + address makerAddress; // Address that created the order. + address takerAddress; // Address that is allowed to fill the order. If set to 0, any address is allowed to fill the order. + address feeRecipientAddress; // Address that will recieve fees when order is filled. + address senderAddress; // Address that is allowed to call Exchange contract methods that affect this order. If set to 0, any address is allowed to call these methods. + uint256 makerAssetAmount; // Amount of makerAsset being offered by maker. Must be greater than 0. + uint256 takerAssetAmount; // Amount of takerAsset being bid on by maker. Must be greater than 0. + uint256 makerFee; // Amount of ZRX paid to feeRecipient by maker when order is filled. If set to 0, no transfer of ZRX from maker to feeRecipient will be attempted. + uint256 takerFee; // Amount of ZRX paid to feeRecipient by taker when order is filled. If set to 0, no transfer of ZRX from taker to feeRecipient will be attempted. + uint256 expirationTimeSeconds; // Timestamp in seconds at which order expires. + uint256 salt; // Arbitrary number to facilitate uniqueness of the order's hash. + bytes makerAssetData; // Encoded data that can be decoded by a specified proxy contract when transferring makerAsset. The last byte references the id of this proxy. + bytes takerAssetData; // Encoded data that can be decoded by a specified proxy contract when transferring takerAsset. The last byte references the id of this proxy. + } + + + /* ============ State Variables ============ */ + address public ZERO_EX_EXCHANGE; + address public ZERO_EX_PROXY; + + + /* ============ Modifiers ============ */ + + + /* ============ Constructor ============ */ + constructor( + address _zeroExExchange, + address _zeroExProxy, + ) + public + { + ZERO_EX_EXCHANGE = _zeroExExchange; + ZERO_EX_PROXY = _zeroExProxy; + } + + + /* ============ Public Functions ============ */ + function exchange( + address tradeOriginator, + bytes orderData + ) + external + returns (uint256) + { + // Parse header for fill Amount + + + Order memory order = parseZeroExOrder(orderData); + + // Move the required takerToken into the wrapper + + // Ensure allowance + } + + /* ============ Getters ============ */ + + + /* ============ Private Helpers ============ */ + + function parseZeroExOrder(bytes orderData) + private + pure + returns (Order memory) + { + Order memory order; + + + + return order; + } + +} From fda4a518bbcce4ebe30fa5c6c67559f7d9f0239c Mon Sep 17 00:00:00 2001 From: felix2feng Date: Fri, 29 Jun 2018 11:44:18 -0700 Subject: [PATCH 2/9] PLanning exchange wrapper --- .../core/external/ZeroExExchangeWrapper.sol | 40 ++++++++++++++----- .../external/zeroExExchangeWrapper.spec.ts | 0 2 files changed, 31 insertions(+), 9 deletions(-) create mode 100644 test/core/external/zeroExExchangeWrapper.spec.ts diff --git a/contracts/core/external/ZeroExExchangeWrapper.sol b/contracts/core/external/ZeroExExchangeWrapper.sol index 14c256302..2aa050a72 100644 --- a/contracts/core/external/ZeroExExchangeWrapper.sol +++ b/contracts/core/external/ZeroExExchangeWrapper.sol @@ -77,9 +77,26 @@ contract ZeroExExchangeWrapper external returns (uint256) { - // Parse header for fill Amount + + // We construct the following to allow calling fillOrder on ZeroEx V2 Exchange + // The layout of this orderData is in the table below. + // + // | Section | Data | Offset | Length | Contents | + // |---------|-----------------|---------------------|-----------------|----------------------------------| + // | Header | signatureLength | 32 | 32 | Num Bytes of 0x Signature Length | + // | | orderLength | 64 | 32 | Num Bytes of 0x Order Length | + // | Body | fillAmount | 96 | 32 | taker asset fill amouint | + // | | signature | 128 | signatureLength | signature in bytes | + // | | order | 128+signatureLength | orderLength | ZeroEx Order | + // Parse fill Amount + + // Slice the signature out. + + // Slice the Order + + // Construct the order Order memory order = parseZeroExOrder(orderData); // Move the required takerToken into the wrapper @@ -92,16 +109,21 @@ contract ZeroExExchangeWrapper /* ============ Private Helpers ============ */ - function parseZeroExOrder(bytes orderData) + /* + * Parses the header of + * Can only be called by authorized contracts. + * + * @param _orderData + * @return [uint256, uint256] The [signatureLength, orderLength] + */ + function parseOrderHeader(bytes _orderData) private pure - returns (Order memory) + returns (uint256 signatureLength, uint256 orderLength) { - Order memory order; - - - - return order; + assembly { + signatureLength := mload(_orderData) + orderLength := mload(add(_orderData, 32)) + } } - } diff --git a/test/core/external/zeroExExchangeWrapper.spec.ts b/test/core/external/zeroExExchangeWrapper.spec.ts new file mode 100644 index 000000000..e69de29bb From a5310b07cc650208bd5fd01fe649b637940fab9f Mon Sep 17 00:00:00 2001 From: felix2feng Date: Fri, 29 Jun 2018 14:06:14 -0700 Subject: [PATCH 3/9] Start work on parse signature --- .../core/external/ZeroExExchangeWrapper.sol | 113 +++++++++++++++--- .../external/zeroExExchangeWrapper.spec.ts | 66 ++++++++++ test/utils/zeroExExchangeWrapper.ts | 34 ++++++ 3 files changed, 195 insertions(+), 18 deletions(-) create mode 100644 test/utils/zeroExExchangeWrapper.ts diff --git a/contracts/core/external/ZeroExExchangeWrapper.sol b/contracts/core/external/ZeroExExchangeWrapper.sol index 2aa050a72..bdda5539a 100644 --- a/contracts/core/external/ZeroExExchangeWrapper.sol +++ b/contracts/core/external/ZeroExExchangeWrapper.sol @@ -15,8 +15,9 @@ */ pragma solidity 0.4.24; +pragma experimental "ABIEncoderV2"; - +import { SafeMath } from "zeppelin-solidity/contracts/math/SafeMath.sol"; /** * @title ZeroExExchangeWrapper @@ -48,6 +49,13 @@ contract ZeroExExchangeWrapper bytes takerAssetData; // Encoded data that can be decoded by a specified proxy contract when transferring takerAsset. The last byte references the id of this proxy. } + struct ZeroExHeader { + uint256 signatureLength; + uint256 orderLength; + uint256 makerAssetDataLength; + uint256 takerAssetDataLength; + } + /* ============ State Variables ============ */ address public ZERO_EX_EXCHANGE; @@ -59,13 +67,13 @@ contract ZeroExExchangeWrapper /* ============ Constructor ============ */ constructor( - address _zeroExExchange, - address _zeroExProxy, + // address _zeroExExchange, + // address _zeroExProxy, ) public { - ZERO_EX_EXCHANGE = _zeroExExchange; - ZERO_EX_PROXY = _zeroExProxy; + // ZERO_EX_EXCHANGE = _zeroExExchange; + // ZERO_EX_PROXY = _zeroExProxy; } @@ -81,49 +89,118 @@ contract ZeroExExchangeWrapper // We construct the following to allow calling fillOrder on ZeroEx V2 Exchange // The layout of this orderData is in the table below. // - // | Section | Data | Offset | Length | Contents | - // |---------|-----------------|---------------------|-----------------|----------------------------------| - // | Header | signatureLength | 32 | 32 | Num Bytes of 0x Signature Length | - // | | orderLength | 64 | 32 | Num Bytes of 0x Order Length | - // | Body | fillAmount | 96 | 32 | taker asset fill amouint | - // | | signature | 128 | signatureLength | signature in bytes | - // | | order | 128+signatureLength | orderLength | ZeroEx Order | + // | Section | Data | Offset | Length | Contents | + // |---------|-----------------------|---------------------|-----------------|-------------------------------| + // | Header | signatureLength | 32 | 32 | Num Bytes of 0x Signature | + // | | orderLength | 64 | 32 | Num Bytes of 0x Order | + // | | makerAssetDataLength | 96 | 32 | Num Bytes of maker asset data | + // | | takerAssetDataLength | 128 | 32 | Num Bytes of taker asset data | + // | Body | fillAmount | 160 | 32 | taker asset fill amouint | + // | | signature | 192 | signatureLength | signature in bytes | + // | | order | 192+signatureLength | orderLength | ZeroEx Order | // Parse fill Amount + uint256 fillAmount = parseFillAmount(_orderData); // Slice the signature out. // Slice the Order // Construct the order - Order memory order = parseZeroExOrder(orderData); + // Order memory order = parseZeroExOrder(orderData); // Move the required takerToken into the wrapper // Ensure allowance + + + return 1; } /* ============ Getters ============ */ - /* ============ Private Helpers ============ */ + /* ============ Test ============ */ + function getSumFromOrderDataHeader(bytes _orderData) + public + pure + returns (uint256) + { + ZeroExHeader memory header = parseOrderHeader(_orderData); + return header.signatureLength + header.orderLength + header.makerAssetDataLength + header.takerAssetDataLength; + } + + function getFillAmount(bytes _orderData) + public + pure + returns (uint256) + { + return parseFillAmount(_orderData); + } + + function getSignature(bytes _orderData) + public + pure + returns (bytes) + { + return parseSignature(_orderData); + } + + /* ============ Private ============ */ + /* * Parses the header of * Can only be called by authorized contracts. * * @param _orderData - * @return [uint256, uint256] The [signatureLength, orderLength] + * @return ZeroExHeader */ function parseOrderHeader(bytes _orderData) private pure - returns (uint256 signatureLength, uint256 orderLength) + returns (ZeroExHeader) { + ZeroExHeader memory header; + assembly { - signatureLength := mload(_orderData) - orderLength := mload(add(_orderData, 32)) + mstore(header, mload(add(_orderData, 32))) // signatureLength + mstore(add(header, 32), mload(add(_orderData, 64))) // orderLength + mstore(add(header, 64), mload(add(_orderData, 96))) // makerAssetDataLength + mstore(add(header, 96), mload(add(_orderData, 128))) // takerAssetDataLength } + + return header; } + + function parseFillAmount(bytes _orderData) + private + pure + returns (uint256 fillAmount) + { + assembly { + fillAmount := mload(add(_orderData, 160)) + } + } + + function parseSignature(bytes _orderData, uint _signatureLength) + private + pure + returns (bytes) + { + bytes signature; + + assembly { + signature := mload() + } + + return signature; + } + + // function parseZeroExOrder(bytes _orderData) + // private + // pure + // returns (Order order); + } diff --git a/test/core/external/zeroExExchangeWrapper.spec.ts b/test/core/external/zeroExExchangeWrapper.spec.ts index e69de29bb..ba9d8d4ec 100644 --- a/test/core/external/zeroExExchangeWrapper.spec.ts +++ b/test/core/external/zeroExExchangeWrapper.spec.ts @@ -0,0 +1,66 @@ +import * as chai from "chai"; +import * as _ from "lodash"; +import * as ethUtil from "ethereumjs-util"; + +import * as ABIDecoder from "abi-decoder"; +import { BigNumber } from "bignumber.js"; + +// Types +import { Address, Bytes32, Log } from "../../../types/common.js"; + +// Contract types +import { ZeroExExchangeWrapperContract } from "../../../types/generated/zero_ex_exchange_wrapper"; + +// Artifacts +const ZeroExExchangeWrapper = artifacts.require("ZeroExExchangeWrapper"); + +import { + generateZeroExExchangeOrdersHeader, +} from "../../utils/zeroExExchangeWrapper"; + +// Testing Set up +import { BigNumberSetup } from "../../config/bigNumberSetup"; +import ChaiSetup from "../../config/chaiSetup"; +BigNumberSetup.configure(); +ChaiSetup.configure(); +const { expect, assert } = chai; + +import { + DEFAULT_GAS, +} from "../../utils/constants"; + +contract("ZeroExExchangeWrapper", (accounts) => { + const [ownerAccount] = accounts; + let zeroExExchangeWrapper: ZeroExExchangeWrapperContract; + + beforeEach(async () => { + const zeroExExchangeWrapperInstance = await ZeroExExchangeWrapper.new( + { from: ownerAccount, gas: DEFAULT_GAS }, + ); + + zeroExExchangeWrapper = new ZeroExExchangeWrapperContract( + web3.eth.contract(zeroExExchangeWrapperInstance.abi).at(zeroExExchangeWrapperInstance.address), + { from: ownerAccount }, + ); + }); + + describe("#getSumFromOrderDataHeader", async () => { + const subjectOrderData: Bytes32 = generateZeroExExchangeOrdersHeader(1, 2, 3, 4); + + it("works", async () => { + const result = await zeroExExchangeWrapper.getSumFromOrderDataHeader.callAsync(subjectOrderData); + expect(result).to.be.bignumber.equal(10); + }); + }); + + describe.only("#getFillAmount", async () => { + const fillAmount = 5; + + const subjectOrderData: Bytes32 = generateZeroExExchangeOrdersHeader(1, 2, 3, 4, fillAmount); + + it("works", async () => { + const result = await zeroExExchangeWrapper.getFillAmount.callAsync(subjectOrderData); + expect(result).to.be.bignumber.equal(fillAmount); + }); + }); +}); diff --git a/test/utils/zeroExExchangeWrapper.ts b/test/utils/zeroExExchangeWrapper.ts new file mode 100644 index 000000000..31c8c0138 --- /dev/null +++ b/test/utils/zeroExExchangeWrapper.ts @@ -0,0 +1,34 @@ +import * as _ from "lodash"; +import * as ethUtil from "ethereumjs-util"; + +import { BigNumber } from "bignumber.js"; +import { Address, Bytes32, UInt } from "../../types/common.js"; +import { + +} from "../utils/constants"; + +function bufferAndLPad32(input: any): Buffer { + return ethUtil.setLengthLeft(ethUtil.toBuffer(input), 32); +} + + + +export function generateZeroExExchangeOrdersHeader( + signatureLength: UInt, + orderLength: UInt, + makerAssetDataLength: UInt, + takerAssetDataLength: UInt, + fillAmount: UInt = 0, +): Bytes32 { + const buffer = Buffer.concat( + [ + bufferAndLPad32(signatureLength), + bufferAndLPad32(orderLength), + bufferAndLPad32(makerAssetDataLength), + bufferAndLPad32(takerAssetDataLength), + bufferAndLPad32(fillAmount), + ] + ); + + return ethUtil.bufferToHex(buffer); +} From 00f388a8a40fa00a14a6b1cc4e9103cc357e6e1c Mon Sep 17 00:00:00 2001 From: felix2feng Date: Fri, 29 Jun 2018 15:56:59 -0700 Subject: [PATCH 4/9] 0x wrapper temp --- .../core/external/ZeroExExchangeWrapper.sol | 120 +-------- .../external/lib/ZeroExOrderDataHandler.sol | 177 +++++++++++++ contracts/external/LibBytes.sol | 1 + .../test/lib/ZeroExOrderDataHandlerLib.sol | 94 +++++++ .../mockZeroExOrderDataHandlerLibrary.spec.ts | 244 ++++++++++++++++++ .../external/zeroExExchangeWrapper.spec.ts | 48 ++-- test/utils/zeroExExchangeWrapper.ts | 93 +++++-- types/common.ts | 20 +- types/zeroEx.d.ts | 25 ++ 9 files changed, 648 insertions(+), 174 deletions(-) create mode 100644 contracts/core/external/lib/ZeroExOrderDataHandler.sol create mode 100644 contracts/test/lib/ZeroExOrderDataHandlerLib.sol create mode 100644 test/core/external/lib/mockZeroExOrderDataHandlerLibrary.spec.ts create mode 100644 types/zeroEx.d.ts diff --git a/contracts/core/external/ZeroExExchangeWrapper.sol b/contracts/core/external/ZeroExExchangeWrapper.sol index bdda5539a..46f57e9d1 100644 --- a/contracts/core/external/ZeroExExchangeWrapper.sol +++ b/contracts/core/external/ZeroExExchangeWrapper.sol @@ -18,6 +18,8 @@ pragma solidity 0.4.24; pragma experimental "ABIEncoderV2"; import { SafeMath } from "zeppelin-solidity/contracts/math/SafeMath.sol"; +import { LibBytes } from "../../external/LibBytes.sol"; +import { ZeroExOrderDataHandler as ZeroEx } from "./lib/ZeroExOrderDataHandler.sol"; /** * @title ZeroExExchangeWrapper @@ -28,33 +30,13 @@ import { SafeMath } from "zeppelin-solidity/contracts/math/SafeMath.sol"; contract ZeroExExchangeWrapper { using SafeMath for uint256; + using LibBytes for bytes; /* ============ Constants ============ */ /* ============ Structs ============ */ - struct Order { - address makerAddress; // Address that created the order. - address takerAddress; // Address that is allowed to fill the order. If set to 0, any address is allowed to fill the order. - address feeRecipientAddress; // Address that will recieve fees when order is filled. - address senderAddress; // Address that is allowed to call Exchange contract methods that affect this order. If set to 0, any address is allowed to call these methods. - uint256 makerAssetAmount; // Amount of makerAsset being offered by maker. Must be greater than 0. - uint256 takerAssetAmount; // Amount of takerAsset being bid on by maker. Must be greater than 0. - uint256 makerFee; // Amount of ZRX paid to feeRecipient by maker when order is filled. If set to 0, no transfer of ZRX from maker to feeRecipient will be attempted. - uint256 takerFee; // Amount of ZRX paid to feeRecipient by taker when order is filled. If set to 0, no transfer of ZRX from taker to feeRecipient will be attempted. - uint256 expirationTimeSeconds; // Timestamp in seconds at which order expires. - uint256 salt; // Arbitrary number to facilitate uniqueness of the order's hash. - bytes makerAssetData; // Encoded data that can be decoded by a specified proxy contract when transferring makerAsset. The last byte references the id of this proxy. - bytes takerAssetData; // Encoded data that can be decoded by a specified proxy contract when transferring takerAsset. The last byte references the id of this proxy. - } - - struct ZeroExHeader { - uint256 signatureLength; - uint256 orderLength; - uint256 makerAssetDataLength; - uint256 takerAssetDataLength; - } /* ============ State Variables ============ */ @@ -86,22 +68,8 @@ contract ZeroExExchangeWrapper returns (uint256) { - // We construct the following to allow calling fillOrder on ZeroEx V2 Exchange - // The layout of this orderData is in the table below. - // - // | Section | Data | Offset | Length | Contents | - // |---------|-----------------------|---------------------|-----------------|-------------------------------| - // | Header | signatureLength | 32 | 32 | Num Bytes of 0x Signature | - // | | orderLength | 64 | 32 | Num Bytes of 0x Order | - // | | makerAssetDataLength | 96 | 32 | Num Bytes of maker asset data | - // | | takerAssetDataLength | 128 | 32 | Num Bytes of taker asset data | - // | Body | fillAmount | 160 | 32 | taker asset fill amouint | - // | | signature | 192 | signatureLength | signature in bytes | - // | | order | 192+signatureLength | orderLength | ZeroEx Order | - - // Parse fill Amount - uint256 fillAmount = parseFillAmount(_orderData); + // uint256 fillAmount = parseFillAmount(_orderData); // Slice the signature out. @@ -121,86 +89,8 @@ contract ZeroExExchangeWrapper /* ============ Getters ============ */ - /* ============ Test ============ */ - function getSumFromOrderDataHeader(bytes _orderData) - public - pure - returns (uint256) - { - ZeroExHeader memory header = parseOrderHeader(_orderData); - return header.signatureLength + header.orderLength + header.makerAssetDataLength + header.takerAssetDataLength; - } - - function getFillAmount(bytes _orderData) - public - pure - returns (uint256) - { - return parseFillAmount(_orderData); - } - - function getSignature(bytes _orderData) - public - pure - returns (bytes) - { - return parseSignature(_orderData); - } + /* ============ Private ============ */ - - /* - * Parses the header of - * Can only be called by authorized contracts. - * - * @param _orderData - * @return ZeroExHeader - */ - function parseOrderHeader(bytes _orderData) - private - pure - returns (ZeroExHeader) - { - ZeroExHeader memory header; - - assembly { - mstore(header, mload(add(_orderData, 32))) // signatureLength - mstore(add(header, 32), mload(add(_orderData, 64))) // orderLength - mstore(add(header, 64), mload(add(_orderData, 96))) // makerAssetDataLength - mstore(add(header, 96), mload(add(_orderData, 128))) // takerAssetDataLength - } - - return header; - } - - function parseFillAmount(bytes _orderData) - private - pure - returns (uint256 fillAmount) - { - assembly { - fillAmount := mload(add(_orderData, 160)) - } - } - - function parseSignature(bytes _orderData, uint _signatureLength) - private - pure - returns (bytes) - { - bytes signature; - - assembly { - signature := mload() - } - - return signature; - } - - // function parseZeroExOrder(bytes _orderData) - // private - // pure - // returns (Order order); - } diff --git a/contracts/core/external/lib/ZeroExOrderDataHandler.sol b/contracts/core/external/lib/ZeroExOrderDataHandler.sol new file mode 100644 index 000000000..dddc21a91 --- /dev/null +++ b/contracts/core/external/lib/ZeroExOrderDataHandler.sol @@ -0,0 +1,177 @@ +/* + Copyright 2018 Set Labs Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +pragma solidity 0.4.24; +pragma experimental "ABIEncoderV2"; + +import { SafeMath } from "zeppelin-solidity/contracts/math/SafeMath.sol"; +import { LibBytes } from "../../../external/LibBytes.sol"; + + +/** + * @title ZeroExOrderDataHandler + * @author Set Protocol + * + * This library contains functions and structs to assist with parsing exchange orders data + */ +library ZeroExOrderDataHandler { + using SafeMath for uint256; + using LibBytes for bytes; + + // ============ Structs ============ + + struct Order { + address makerAddress; // Address that created the order. + address takerAddress; // Address that is allowed to fill the order. If set to 0, any address is allowed to fill the order. + address feeRecipientAddress; // Address that will recieve fees when order is filled. + address senderAddress; // Address that is allowed to call Exchange contract methods that affect this order. If set to 0, any address is allowed to call these methods. + uint256 makerAssetAmount; // Amount of makerAsset being offered by maker. Must be greater than 0. + uint256 takerAssetAmount; // Amount of takerAsset being bid on by maker. Must be greater than 0. + uint256 makerFee; // Amount of ZRX paid to feeRecipient by maker when order is filled. If set to 0, no transfer of ZRX from maker to feeRecipient will be attempted. + uint256 takerFee; // Amount of ZRX paid to feeRecipient by taker when order is filled. If set to 0, no transfer of ZRX from taker to feeRecipient will be attempted. + uint256 expirationTimeSeconds; // Timestamp in seconds at which order expires. + uint256 salt; // Arbitrary number to facilitate uniqueness of the order's hash. + bytes makerAssetData; // Encoded data that can be decoded by a specified proxy contract when transferring makerAsset. The last byte references the id of this proxy. + bytes takerAssetData; // Encoded data that can be decoded by a specified proxy contract when transferring takerAsset. The last byte references the id of this proxy. + } + + struct ZeroExHeader { + uint256 signatureLength; + uint256 orderLength; + uint256 makerAssetDataLength; + uint256 takerAssetDataLength; + } + + // ============ Internal Functions ============ + + // We construct the following to allow calling fillOrder on ZeroEx V2 Exchange + // The layout of this orderData is in the table below. + // + // | Section | Data | Offset | Length | Contents | + // |---------|-----------------------|---------------------|-----------------|-------------------------------| + // | Header | signatureLength | 0 | 32 | Num Bytes of 0x Signature | + // | | orderLength | 32 | 32 | Num Bytes of 0x Order | + // | | makerAssetDataLength | 64 | 32 | Num Bytes of maker asset data | + // | | takerAssetDataLength | 96 | 32 | Num Bytes of taker asset data | + // | Body | fillAmount | 128 | 32 | taker asset fill amouint | + // | | signature | 160 | signatureLength | signature in bytes | + // | | order | 160+signatureLength | orderLength | ZeroEx Order | + + /* + * Parses the header of + * Can only be called by authorized contracts. + * + * @param _orderData + * @return ZeroExHeader + */ + function parseOrderHeader(bytes _orderData) + internal + pure + returns (ZeroExHeader) + { + ZeroExHeader memory header; + + uint256 orderDataAddr = _orderData.contentAddress(); + + assembly { + mstore(header, mload(orderDataAddr)) // signatureLength + mstore(add(header, 32), mload(add(orderDataAddr, 32))) // orderLength + mstore(add(header, 64), mload(add(orderDataAddr, 64))) // makerAssetDataLength + mstore(add(header, 96), mload(add(orderDataAddr, 96))) // takerAssetDataLength + } + + return header; + } + + function parseFillAmount(bytes _orderData) + internal + pure + returns (uint256 fillAmount) + { + uint256 orderDataAddr = _orderData.contentAddress(); + + assembly { + fillAmount := mload(add(orderDataAddr, 128)) + } + } + + function sliceSignature(bytes _orderData, uint _signatureLength) + internal + pure + returns (bytes) + { + bytes memory signature = _orderData.slice(160, _signatureLength.add(160)); + return signature; + } + + function sliceZeroExOrder(bytes _orderData, uint _signatureLength, uint _orderLength) + internal + pure + returns (bytes) + { + uint256 orderDataAddr = _orderData.contentAddress(); + uint256 orderStartAddress = orderDataAddr.add(_signatureLength); + bytes memory order = _orderData.slice(orderStartAddress, orderStartAddress.add(_orderLength)); + return order; + } + + function parseZeroExOrder(bytes _zeroExOrder, uint _makerAssetDataLength, uint _takerAssetDataLength) + internal + pure + returns (Order memory) + { + + Order memory order; + uint256 orderDataAddr = _zeroExOrder.contentAddress(); + + // Take zeroEx order in bytes and return a 0x order struct + assembly { + mstore(order, mload(orderDataAddr)) // maker + mstore(add(order, 32), mload(add(orderDataAddr, 32))) // taker + mstore(add(order, 64), mload(add(orderDataAddr, 64))) // feeRecipient + mstore(add(order, 96), mload(add(orderDataAddr, 96))) // senderAddress + mstore(add(order, 128), mload(add(orderDataAddr, 128))) // makerAssetAmount + mstore(add(order, 160), mload(add(orderDataAddr, 160))) // takerAssetAmount + mstore(add(order, 192), mload(add(orderDataAddr, 192))) // makerFee + mstore(add(order, 224), mload(add(orderDataAddr, 224))) // takerFee + mstore(add(order, 256), mload(add(orderDataAddr, 256))) // expirationUnixTimestampSec + mstore(add(order, 288), mload(add(orderDataAddr, 288))) // salt + } + + order.makerAssetData = _zeroExOrder.slice(320, _makerAssetDataLength.add(320)); + order.takerAssetData = _zeroExOrder.slice(_makerAssetDataLength.add(320), _makerAssetDataLength.add(320).add(_takerAssetDataLength)); + + return order; + } + + function parseZeroExOrderData(bytes _orderData) + internal + pure + returns(Order memory) + { + ZeroExHeader memory header = parseOrderHeader(_orderData); + + uint fillAmount = parseFillAmount(_orderData); + bytes memory signature = sliceSignature(_orderData, header.signatureLength); + Order memory order = parseZeroExOrder( + sliceZeroExOrder(_orderData, header.signatureLength, header.orderLength), + header.makerAssetDataLength, + header.takerAssetDataLength + ); + + return order; + } +} diff --git a/contracts/external/LibBytes.sol b/contracts/external/LibBytes.sol index 0c6be6f23..1324a6637 100644 --- a/contracts/external/LibBytes.sol +++ b/contracts/external/LibBytes.sol @@ -179,6 +179,7 @@ library LibBytes { "FROM_LESS_THAN_TO_REQUIRED" ); require( + // NOTE: Set Protocol changed from `to < b.length` so that the last byte can be sliced off to <= b.length, "TO_LESS_THAN_LENGTH_REQUIRED" ); diff --git a/contracts/test/lib/ZeroExOrderDataHandlerLib.sol b/contracts/test/lib/ZeroExOrderDataHandlerLib.sol new file mode 100644 index 000000000..d3c343102 --- /dev/null +++ b/contracts/test/lib/ZeroExOrderDataHandlerLib.sol @@ -0,0 +1,94 @@ +pragma solidity 0.4.24; +pragma experimental "ABIEncoderV2"; + +import { ZeroExOrderDataHandler } from "../../core/external/lib/ZeroExOrderDataHandler.sol"; + + +// Mock class implementing internal OrderHandler methods +contract MockZeroExOrderDataHandlerLibrary { + function getOrderDataHeader(bytes _orderData) + public + pure + returns (uint256[4]) + { + ZeroExOrderDataHandler.ZeroExHeader memory header = ZeroExOrderDataHandler.parseOrderHeader(_orderData); + return [ + header.signatureLength, + header.orderLength, + header.makerAssetDataLength, + header.takerAssetDataLength + ]; + } + + function getFillAmount(bytes _orderData) + public + pure + returns (uint256) + { + return ZeroExOrderDataHandler.parseFillAmount(_orderData); + } + + function getSignature(bytes _orderData) + public + pure + returns (bytes) + { + ZeroExOrderDataHandler.ZeroExHeader memory header = ZeroExOrderDataHandler.parseOrderHeader(_orderData); + uint256 signatureLength = header.signatureLength; + return ZeroExOrderDataHandler.sliceSignature(_orderData, signatureLength); + } + + function parseZeroExOrder(bytes _zeroExOrder, uint _makerAssetDataLength, uint _takerAssetDataLength) + public + pure + returns (address[4], uint256[6], bytes, bytes) + { + ZeroExOrderDataHandler.Order memory order = ZeroExOrderDataHandler.parseZeroExOrder(_zeroExOrder, _makerAssetDataLength, _takerAssetDataLength); + + return ( + [ + order.makerAddress, + order.takerAddress, + order.feeRecipientAddress, + order.senderAddress + ], + [ + order.makerAssetAmount, + order.takerAssetAmount, + order.makerFee, + order.takerFee, + order.expirationTimeSeconds, + order.salt + ], + order.makerAssetData, + order.takerAssetData + ); + } + + function parseZeroExOrderData(bytes _orderData) + public + pure + returns(address[4], uint256[6], bytes, bytes) + { + ZeroExOrderDataHandler.Order memory order = ZeroExOrderDataHandler.parseZeroExOrderData(_orderData); + + return ( + [ + order.makerAddress, + order.takerAddress, + order.feeRecipientAddress, + order.senderAddress + ], + [ + order.makerAssetAmount, + order.takerAssetAmount, + order.makerFee, + order.takerFee, + order.expirationTimeSeconds, + order.salt + ], + order.makerAssetData, + order.takerAssetData + ); + } +} diff --git a/test/core/external/lib/mockZeroExOrderDataHandlerLibrary.spec.ts b/test/core/external/lib/mockZeroExOrderDataHandlerLibrary.spec.ts new file mode 100644 index 000000000..f18056f1d --- /dev/null +++ b/test/core/external/lib/mockZeroExOrderDataHandlerLibrary.spec.ts @@ -0,0 +1,244 @@ +import * as chai from "chai"; +import * as _ from "lodash"; +import * as ethUtil from "ethereumjs-util"; + +import * as ABIDecoder from "abi-decoder"; +import { BigNumber } from "bignumber.js"; + +// Types +import { Address, Bytes32, Log, UInt } from "../../../../types/common.js"; +import { ZeroExSignature, ZeroExOrderHeader, ZeroExOrder } from "../../../../types/zeroEx"; + +// Contract types +import { MockZeroExOrderDataHandlerLibraryContract } from "../../../../types/generated/mock_zero_ex_order_data_handler_library"; + +// Artifacts +const MockZeroExOrderDataHandlerLibrary = artifacts.require("MockZeroExOrderDataHandlerLibrary"); + +import { + bufferOrderHeader, + bufferFillAmount, + bufferSignature, + bufferZeroExOrder, + bufferArrayToHex, + createZeroExOrder, +} from "../../../utils/zeroExExchangeWrapper"; + +// Testing Set up +import { BigNumberSetup } from "../../../config/bigNumberSetup"; +import ChaiSetup from "../../../config/chaiSetup"; +BigNumberSetup.configure(); +ChaiSetup.configure(); +const { expect, assert } = chai; + +import { + DEFAULT_GAS, +} from "../../../utils/constants"; + +contract("MockZeroExOrderDataHandlerLibrary", (accounts) => { + const [ownerAccount, takerAddress, feeRecipientAddress, senderAddress] = accounts; + let zeroExExchangeWrapper: MockZeroExOrderDataHandlerLibraryContract; + + // Signature + let signature: ZeroExSignature = "ABCDEFgiHIJKLMNOPQRSTUVWXYZ"; + + // 0x Order Subject Data + let fillAmount = new BigNumber(5); + + let makerAssetAmount = new BigNumber(1); + let takerAssetAmount = new BigNumber(2); + let makerFee = new BigNumber(3); + let takerFee = new BigNumber(4); + let expirationTimeSeconds = new BigNumber(5); + let salt = new BigNumber(6); + let makerAssetData = "ABC"; + let takerAssetData = "XYZ"; + + let zeroExOrder: ZeroExOrder = createZeroExOrder( + ownerAccount, + takerAddress, + feeRecipientAddress, + senderAddress, + makerAssetAmount, + takerAssetAmount, + makerFee, + takerFee, + expirationTimeSeconds, + salt, + makerAssetData, + takerAssetData, + ); + + // Header Subject Data + let signatureLength: UInt = new BigNumber(signature.length); + let zeroExOrderLength = new BigNumber(0); + let makerAssetDataLength = new BigNumber(makerAssetData.length); + let takerAssetDataLength = new BigNumber(takerAssetData.length); + + beforeEach(async () => { + const zeroExExchangeWrapperInstance = await MockZeroExOrderDataHandlerLibrary.new( + { from: ownerAccount, gas: DEFAULT_GAS }, + ); + + zeroExExchangeWrapper = new MockZeroExOrderDataHandlerLibraryContract( + web3.eth.contract(zeroExExchangeWrapperInstance.abi).at(zeroExExchangeWrapperInstance.address), + { from: ownerAccount }, + ); + }); + + describe("#getOrderDataHeader", async () => { + let subjectOrderData: Bytes32; + + beforeEach(async () => { + subjectOrderData = bufferArrayToHex(bufferOrderHeader( + signatureLength, + zeroExOrderLength, + makerAssetDataLength, + takerAssetDataLength + )); + }); + + it("works", async () => { + const result = await zeroExExchangeWrapper.getOrderDataHeader.callAsync(subjectOrderData); + + expect(result[0]).to.bignumber.equal(signatureLength); + expect(result[1]).to.bignumber.equal(zeroExOrderLength); + expect(result[2]).to.bignumber.equal(makerAssetDataLength); + expect(result[3]).to.bignumber.equal(takerAssetDataLength); + }); + }); + + describe("#getFillAmount", async () => { + let subjectOrderData: Bytes32; + + beforeEach(async () => { + subjectOrderData = bufferArrayToHex( + bufferOrderHeader( + signatureLength, + zeroExOrderLength, + makerAssetDataLength, + takerAssetDataLength, + ).concat(bufferFillAmount(fillAmount) + )); + }); + + it("works", async () => { + const result = await zeroExExchangeWrapper.getFillAmount.callAsync(subjectOrderData); + expect(result).to.be.bignumber.equal(fillAmount); + }); + }); + + describe("#getSignature", async () => { + let subjectOrderData: Bytes32; + + beforeEach(async () => { + subjectOrderData = bufferArrayToHex( + bufferOrderHeader( + signatureLength, + zeroExOrderLength, + makerAssetDataLength, + takerAssetDataLength, + ) + .concat(bufferFillAmount(fillAmount)) + .concat(bufferSignature(signature) + )); + }); + + it("works", async () => { + const result = await zeroExExchangeWrapper.getSignature.callAsync(subjectOrderData); + expect(web3.toAscii(result)).to.equal(signature); + }); + }); + + describe("#parseZeroExOrder", async () => { + let subjectOrderData: Bytes32; + + beforeEach(async () => { + const zeroExOrderBuffer = bufferZeroExOrder(zeroExOrder); + + subjectOrderData = bufferArrayToHex(zeroExOrderBuffer); + }); + + it("works", async () => { + const result = await zeroExExchangeWrapper.parseZeroExOrder.callAsync( + subjectOrderData, + makerAssetDataLength, + takerAssetDataLength + ); + + const [addresses, uints, makerAssetDataResult, takerAssetDataResult] = result; + const [makerResult, takerResult, feeRecipientResult, senderResult] = addresses; + const [ + makerAssetAmountResult, + takerAssetAmountResult, + makerFeeResult, + takerFeeResult, + expirationResult, + saltResult, + ] = uints; + + expect(ownerAccount).to.equal(makerResult); + expect(takerAddress).to.equal(takerResult); + expect(feeRecipientAddress).to.equal(feeRecipientResult); + expect(senderAddress).to.equal(senderResult); + expect(makerAssetAmount).to.be.bignumber.equal(makerAssetAmountResult); + expect(takerAssetAmount).to.be.bignumber.equal(takerAssetAmountResult); + expect(makerFee).to.be.bignumber.equal(makerFeeResult); + expect(takerFee).to.be.bignumber.equal(takerFeeResult); + expect(expirationTimeSeconds).to.be.bignumber.equal(expirationResult); + expect(salt).to.be.bignumber.equal(saltResult); + expect(makerAssetData).to.equal(web3.toAscii(makerAssetDataResult)); + expect(takerAssetData).to.equal(web3.toAscii(takerAssetDataResult)); + }); + }); + + describe("#parseZeroExOrderData", async () => { + let subjectOrderData: Bytes32; + + beforeEach(async () => { + const zeroExOrderBuffer = bufferZeroExOrder(zeroExOrder); + + zeroExOrderLength = new BigNumber(Buffer.concat(zeroExOrderBuffer).length); + + subjectOrderData = bufferArrayToHex( + bufferOrderHeader( + signatureLength, + zeroExOrderLength, + makerAssetDataLength, + takerAssetDataLength, + ) + .concat(bufferFillAmount(fillAmount)) + .concat(bufferSignature(signature)) + .concat(zeroExOrderBuffer) + ); + }); + + it("works", async () => { + const result = await zeroExExchangeWrapper.parseZeroExOrderData.callAsync(subjectOrderData); + + const [addresses, uints, makerAssetDataResult, takerAssetDataResult] = result; + const [makerResult, takerResult, feeRecipientResult, senderResult] = addresses; + const [ + makerAssetAmountResult, + takerAssetAmountResult, + makerFeeResult, + takerFeeResult, + expirationResult, + saltResult, + ] = uints; + + expect(ownerAccount).to.equal(makerResult); + expect(takerAddress).to.equal(takerResult); + expect(feeRecipientAddress).to.equal(feeRecipientResult); + expect(senderAddress).to.equal(senderResult); + expect(makerAssetAmount).to.be.bignumber.equal(makerAssetAmountResult); + expect(takerAssetAmount).to.be.bignumber.equal(takerAssetAmountResult); + expect(makerFee).to.be.bignumber.equal(makerFeeResult); + expect(takerFee).to.be.bignumber.equal(takerFeeResult); + expect(expirationTimeSeconds).to.be.bignumber.equal(expirationResult); + expect(salt).to.be.bignumber.equal(saltResult); + expect(makerAssetData).to.equal(web3.toAscii(makerAssetDataResult)); + expect(takerAssetData).to.equal(web3.toAscii(takerAssetDataResult)); + }); + }); +}); diff --git a/test/core/external/zeroExExchangeWrapper.spec.ts b/test/core/external/zeroExExchangeWrapper.spec.ts index ba9d8d4ec..e49321aa6 100644 --- a/test/core/external/zeroExExchangeWrapper.spec.ts +++ b/test/core/external/zeroExExchangeWrapper.spec.ts @@ -6,7 +6,8 @@ import * as ABIDecoder from "abi-decoder"; import { BigNumber } from "bignumber.js"; // Types -import { Address, Bytes32, Log } from "../../../types/common.js"; +import { Address, Bytes32, Log, UInt } from "../../../types/common.js"; +import { ZeroExSignature, ZeroExOrderHeader, ZeroExOrder } from "../../../types/zeroEx"; // Contract types import { ZeroExExchangeWrapperContract } from "../../../types/generated/zero_ex_exchange_wrapper"; @@ -15,7 +16,12 @@ import { ZeroExExchangeWrapperContract } from "../../../types/generated/zero_ex_ const ZeroExExchangeWrapper = artifacts.require("ZeroExExchangeWrapper"); import { - generateZeroExExchangeOrdersHeader, + bufferOrderHeader, + bufferFillAmount, + bufferSignature, + bufferZeroExOrder, + bufferArrayToHex, + createZeroExOrder, } from "../../utils/zeroExExchangeWrapper"; // Testing Set up @@ -28,11 +34,25 @@ const { expect, assert } = chai; import { DEFAULT_GAS, } from "../../utils/constants"; - + contract("ZeroExExchangeWrapper", (accounts) => { - const [ownerAccount] = accounts; + const [ownerAccount, takerAddress, feeRecipientAddress, senderAddress] = accounts; let zeroExExchangeWrapper: ZeroExExchangeWrapperContract; + + let signature: ZeroExSignature = "ABCDEFHIJKLMNOPQRSTUVWXYZ"; + let signatureLength: UInt = signature.length; + + let zeroExOrder: ZeroExOrder; + let zeroExOrderLength = 0; + + let fillAmount = 5; + + let makerAssetDataLength = 4; + let takerAssetDataLength = 3; + + + beforeEach(async () => { const zeroExExchangeWrapperInstance = await ZeroExExchangeWrapper.new( { from: ownerAccount, gas: DEFAULT_GAS }, @@ -43,24 +63,4 @@ contract("ZeroExExchangeWrapper", (accounts) => { { from: ownerAccount }, ); }); - - describe("#getSumFromOrderDataHeader", async () => { - const subjectOrderData: Bytes32 = generateZeroExExchangeOrdersHeader(1, 2, 3, 4); - - it("works", async () => { - const result = await zeroExExchangeWrapper.getSumFromOrderDataHeader.callAsync(subjectOrderData); - expect(result).to.be.bignumber.equal(10); - }); - }); - - describe.only("#getFillAmount", async () => { - const fillAmount = 5; - - const subjectOrderData: Bytes32 = generateZeroExExchangeOrdersHeader(1, 2, 3, 4, fillAmount); - - it("works", async () => { - const result = await zeroExExchangeWrapper.getFillAmount.callAsync(subjectOrderData); - expect(result).to.be.bignumber.equal(fillAmount); - }); - }); }); diff --git a/test/utils/zeroExExchangeWrapper.ts b/test/utils/zeroExExchangeWrapper.ts index 31c8c0138..8684f3af2 100644 --- a/test/utils/zeroExExchangeWrapper.ts +++ b/test/utils/zeroExExchangeWrapper.ts @@ -1,34 +1,95 @@ import * as _ from "lodash"; import * as ethUtil from "ethereumjs-util"; +import * as Web3 from "web3"; +const web3 = new Web3(); import { BigNumber } from "bignumber.js"; -import { Address, Bytes32, UInt } from "../../types/common.js"; -import { - -} from "../utils/constants"; +import { Address, Bytes32, Bytes, UInt } from "../../types/common.js"; +import { ZeroExOrder } from "../../types/zeroEx"; function bufferAndLPad32(input: any): Buffer { return ethUtil.setLengthLeft(ethUtil.toBuffer(input), 32); } +export function createZeroExOrder( + makerAddress: Address, + takerAddress: Address, + feeRecipientAddress: Address, + senderAddress: Address, + makerAssetAmount: UInt, + takerAssetAmount: UInt, + makerFee: UInt, + takerFee: UInt, + expirationTimeSeconds: UInt, + salt: UInt, + makerAssetData: Bytes, + takerAssetData: Bytes, +): ZeroExOrder { + return { + makerAddress, + takerAddress, + feeRecipientAddress, + senderAddress, + makerAssetAmount, + takerAssetAmount, + makerFee, + takerFee, + expirationTimeSeconds, + salt, + makerAssetData, + takerAssetData, + } +} -export function generateZeroExExchangeOrdersHeader( +export function bufferOrderHeader( signatureLength: UInt, orderLength: UInt, makerAssetDataLength: UInt, takerAssetDataLength: UInt, - fillAmount: UInt = 0, -): Bytes32 { - const buffer = Buffer.concat( - [ - bufferAndLPad32(signatureLength), - bufferAndLPad32(orderLength), - bufferAndLPad32(makerAssetDataLength), - bufferAndLPad32(takerAssetDataLength), - bufferAndLPad32(fillAmount), +): Buffer[] { + return [ + bufferAndLPad32(web3.toHex(signatureLength)), + bufferAndLPad32(web3.toHex(orderLength)), + bufferAndLPad32(web3.toHex(makerAssetDataLength)), + bufferAndLPad32(web3.toHex(takerAssetDataLength)), ] - ); +} + +export function bufferFillAmount( + fillAmount: UInt = 0, +): Buffer[] { + return [bufferAndLPad32(web3.toHex(fillAmount))]; +} + +export function bufferSignature( + signature: Bytes32 = '', +): Buffer[] { + return [ethUtil.toBuffer(signature)]; +} - return ethUtil.bufferToHex(buffer); +export function bufferZeroExOrder( + order: ZeroExOrder, +): Buffer[] { + return [ + bufferAndLPad32(order.makerAddress), + bufferAndLPad32(order.takerAddress), + bufferAndLPad32(order.feeRecipientAddress), + bufferAndLPad32(order.senderAddress), + bufferAndLPad32(web3.toHex(order.makerAssetAmount)), + bufferAndLPad32(web3.toHex(order.takerAssetAmount)), + bufferAndLPad32(web3.toHex(order.makerFee)), + bufferAndLPad32(web3.toHex(order.takerFee)), + bufferAndLPad32(web3.toHex(order.expirationTimeSeconds)), + bufferAndLPad32(web3.toHex(order.salt)), + ethUtil.toBuffer(order.makerAssetData), + ethUtil.toBuffer(order.takerAssetData), + ]; +} + +export function bufferArrayToHex( + bufferArr: Buffer[] +): Bytes32 { + const buffer = Buffer.concat(bufferArr); + return ethUtil.bufferToHex(buffer); } diff --git a/types/common.ts b/types/common.ts index b01c8ab01..e3338a811 100644 --- a/types/common.ts +++ b/types/common.ts @@ -29,18 +29,6 @@ export interface TxDataPayable extends TxData { value?: BigNumber; } -export interface IssuanceOrder { - setAddress: Address, - quantity: BigNumber, - makerAddress: Address, - makerToken: Address, - makerTokenAmount: BigNumber, - expiration: BigNumber, - relayerToken: Address, - relayerTokenAmount: BigNumber, - salt: BigNumber -} - export interface Log { event: string; address: Address; @@ -50,10 +38,4 @@ export interface Log { export type Address = string; export type UInt = number | BigNumber; export type Bytes32 = string; - -export enum SolidityTypes { - Address = 'address', - Uint256 = 'uint256', - Uint8 = 'uint8', - Uint = 'uint', -} +export type Bytes = string; diff --git a/types/zeroEx.d.ts b/types/zeroEx.d.ts new file mode 100644 index 000000000..e378fc4de --- /dev/null +++ b/types/zeroEx.d.ts @@ -0,0 +1,25 @@ +import { Address, Bytes, UInt } from "./common"; + +export interface ZeroExOrderHeader { + signatureLength: UInt; + orderLength: UInt; + makerAssetDataLength: UInt; + takerAssetDataLength: UInt; +} + +export interface ZeroExOrder { + makerAddress: Address; + takerAddress: Address; + feeRecipientAddress: Address; + senderAddress: Address; + makerAssetAmount: UInt; + takerAssetAmount: UInt; + makerFee: UInt; + takerFee: UInt; + expirationTimeSeconds: UInt; + salt: UInt; + makerAssetData: Bytes; + takerAssetData: Bytes; +} + +export type ZeroExSignature = string; From b667789db966665fab2ebe884167f04cf3f73f4d Mon Sep 17 00:00:00 2001 From: felix2feng Date: Mon, 2 Jul 2018 21:43:56 -0700 Subject: [PATCH 5/9] Update common.ts from master --- types/common.ts | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/types/common.ts b/types/common.ts index e3338a811..1941421f3 100644 --- a/types/common.ts +++ b/types/common.ts @@ -29,6 +29,18 @@ export interface TxDataPayable extends TxData { value?: BigNumber; } +export interface IssuanceOrder { + setAddress: Address, + quantity: BigNumber, + makerAddress: Address, + makerToken: Address, + makerTokenAmount: BigNumber, + expiration: BigNumber, + relayerToken: Address, + relayerTokenAmount: BigNumber, + salt: BigNumber +} + export interface Log { event: string; address: Address; @@ -39,3 +51,10 @@ export type Address = string; export type UInt = number | BigNumber; export type Bytes32 = string; export type Bytes = string; + +export enum SolidityTypes { + Address = 'address', + Uint256 = 'uint256', + Uint8 = 'uint8', + Uint = 'uint', +} From cb37ba448eb3787fa97ce9734fc50327e8354f65 Mon Sep 17 00:00:00 2001 From: felix2feng Date: Mon, 2 Jul 2018 22:36:49 -0700 Subject: [PATCH 6/9] Write some tests --- .../core/external/ZeroExExchangeWrapper.sol | 14 ++--- .../external/lib/ZeroExOrderDataHandler.sol | 21 +++++++- .../test/lib/ZeroExOrderDataHandlerLib.sol | 33 ++---------- package.json | 2 +- .../mockZeroExOrderDataHandlerLibrary.spec.ts | 54 +++---------------- .../external/zeroExExchangeWrapper.spec.ts | 14 ----- 6 files changed, 32 insertions(+), 106 deletions(-) diff --git a/contracts/core/external/ZeroExExchangeWrapper.sol b/contracts/core/external/ZeroExExchangeWrapper.sol index 46f57e9d1..828a9e2c1 100644 --- a/contracts/core/external/ZeroExExchangeWrapper.sol +++ b/contracts/core/external/ZeroExExchangeWrapper.sol @@ -30,24 +30,15 @@ import { ZeroExOrderDataHandler as ZeroEx } from "./lib/ZeroExOrderDataHandler.s contract ZeroExExchangeWrapper { using SafeMath for uint256; - using LibBytes for bytes; - - /* ============ Constants ============ */ - - - - /* ============ Structs ============ */ - /* ============ State Variables ============ */ + address public ZERO_EX_EXCHANGE; address public ZERO_EX_PROXY; - /* ============ Modifiers ============ */ - - /* ============ Constructor ============ */ + constructor( // address _zeroExExchange, // address _zeroExProxy, @@ -60,6 +51,7 @@ contract ZeroExExchangeWrapper /* ============ Public Functions ============ */ + function exchange( address tradeOriginator, bytes orderData diff --git a/contracts/core/external/lib/ZeroExOrderDataHandler.sol b/contracts/core/external/lib/ZeroExOrderDataHandler.sol index dddc21a91..560e9a579 100644 --- a/contracts/core/external/lib/ZeroExOrderDataHandler.sol +++ b/contracts/core/external/lib/ZeroExOrderDataHandler.sol @@ -71,7 +71,7 @@ library ZeroExOrderDataHandler { // | | order | 160+signatureLength | orderLength | ZeroEx Order | /* - * Parses the header of + * Parses the header of the orderData * Can only be called by authorized contracts. * * @param _orderData @@ -137,7 +137,24 @@ library ZeroExOrderDataHandler { Order memory order; uint256 orderDataAddr = _zeroExOrder.contentAddress(); - // Take zeroEx order in bytes and return a 0x order struct + + // | 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 | - NOT IMPLEMENTED + // | | 0x1A0 | ** | makerAssetData Contents | - NOT IMPLEMENTED + // | | 0x1C0 | 32 | takerAssetData Length | - NOT IMPLEMENTED + // | | 0x1E0 | ** | takerAssetData Contents | - NOT IMPLEMENTED assembly { mstore(order, mload(orderDataAddr)) // maker mstore(add(order, 32), mload(add(orderDataAddr, 32))) // taker diff --git a/contracts/test/lib/ZeroExOrderDataHandlerLib.sol b/contracts/test/lib/ZeroExOrderDataHandlerLib.sol index d3c343102..7bf64e2a9 100644 --- a/contracts/test/lib/ZeroExOrderDataHandlerLib.sol +++ b/contracts/test/lib/ZeroExOrderDataHandlerLib.sol @@ -6,7 +6,7 @@ import { ZeroExOrderDataHandler } from "../../core/external/lib/ZeroExOrderDataH // Mock class implementing internal OrderHandler methods contract MockZeroExOrderDataHandlerLibrary { - function getOrderDataHeader(bytes _orderData) + function parseOrderDataHeader(bytes _orderData) public pure returns (uint256[4]) @@ -20,7 +20,7 @@ contract MockZeroExOrderDataHandlerLibrary { ]; } - function getFillAmount(bytes _orderData) + function parseFillAmount(bytes _orderData) public pure returns (uint256) @@ -28,7 +28,7 @@ contract MockZeroExOrderDataHandlerLibrary { return ZeroExOrderDataHandler.parseFillAmount(_orderData); } - function getSignature(bytes _orderData) + function parseSignature(bytes _orderData) public pure returns (bytes) @@ -38,33 +38,6 @@ contract MockZeroExOrderDataHandlerLibrary { return ZeroExOrderDataHandler.sliceSignature(_orderData, signatureLength); } - function parseZeroExOrder(bytes _zeroExOrder, uint _makerAssetDataLength, uint _takerAssetDataLength) - public - pure - returns (address[4], uint256[6], bytes, bytes) - { - ZeroExOrderDataHandler.Order memory order = ZeroExOrderDataHandler.parseZeroExOrder(_zeroExOrder, _makerAssetDataLength, _takerAssetDataLength); - - return ( - [ - order.makerAddress, - order.takerAddress, - order.feeRecipientAddress, - order.senderAddress - ], - [ - order.makerAssetAmount, - order.takerAssetAmount, - order.makerFee, - order.takerFee, - order.expirationTimeSeconds, - order.salt - ], - order.makerAssetData, - order.takerAssetData - ); - } - function parseZeroExOrderData(bytes _orderData) public pure diff --git a/package.json b/package.json index 0d97ae794..1f78b6268 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "clean": "rm -rf build; rm -rf transpiled; rm -rf types/generated", "compile": "truffle compile", "prepare-test": "yarn clean && truffle compile --all && yarn run generate-typings && yarn run transpile", - "test": "yarn prepare-test && truffle test `find ./transpiled/test -name '*.spec.js'`", + "test": "yarn prepare-test && truffle test `find ./transpiled/test -name 'mockZeroExOrderDataHandlerLibrary.spec.js'`", "transpile": "tsc", "generate-typings": "abi-gen --abis './build/contracts/*.json' --out './types/generated' --template './types/contract_templates/contract.mustache' --partials './types/contract_templates/partials/*.mustache' && yarn run rename-generated-abi", "rename-generated-abi": "mv types/generated/detailed_e_r_c20.ts types/generated/detailed_erc20.ts && mv types/generated/e_r_c20_basic.ts types/generated/erc20_basic.ts", diff --git a/test/core/external/lib/mockZeroExOrderDataHandlerLibrary.spec.ts b/test/core/external/lib/mockZeroExOrderDataHandlerLibrary.spec.ts index f18056f1d..6316fb4f0 100644 --- a/test/core/external/lib/mockZeroExOrderDataHandlerLibrary.spec.ts +++ b/test/core/external/lib/mockZeroExOrderDataHandlerLibrary.spec.ts @@ -86,7 +86,7 @@ contract("MockZeroExOrderDataHandlerLibrary", (accounts) => { ); }); - describe("#getOrderDataHeader", async () => { + describe("#parseOrderDataHeader", async () => { let subjectOrderData: Bytes32; beforeEach(async () => { @@ -99,7 +99,7 @@ contract("MockZeroExOrderDataHandlerLibrary", (accounts) => { }); it("works", async () => { - const result = await zeroExExchangeWrapper.getOrderDataHeader.callAsync(subjectOrderData); + const result = await zeroExExchangeWrapper.parseOrderDataHeader.callAsync(subjectOrderData); expect(result[0]).to.bignumber.equal(signatureLength); expect(result[1]).to.bignumber.equal(zeroExOrderLength); @@ -108,7 +108,7 @@ contract("MockZeroExOrderDataHandlerLibrary", (accounts) => { }); }); - describe("#getFillAmount", async () => { + describe("#parseFillAmount", async () => { let subjectOrderData: Bytes32; beforeEach(async () => { @@ -123,12 +123,12 @@ contract("MockZeroExOrderDataHandlerLibrary", (accounts) => { }); it("works", async () => { - const result = await zeroExExchangeWrapper.getFillAmount.callAsync(subjectOrderData); + const result = await zeroExExchangeWrapper.parseFillAmount.callAsync(subjectOrderData); expect(result).to.be.bignumber.equal(fillAmount); }); }); - describe("#getSignature", async () => { + describe("#parseSignature", async () => { let subjectOrderData: Bytes32; beforeEach(async () => { @@ -145,53 +145,11 @@ contract("MockZeroExOrderDataHandlerLibrary", (accounts) => { }); it("works", async () => { - const result = await zeroExExchangeWrapper.getSignature.callAsync(subjectOrderData); + const result = await zeroExExchangeWrapper.parseSignature.callAsync(subjectOrderData); expect(web3.toAscii(result)).to.equal(signature); }); }); - describe("#parseZeroExOrder", async () => { - let subjectOrderData: Bytes32; - - beforeEach(async () => { - const zeroExOrderBuffer = bufferZeroExOrder(zeroExOrder); - - subjectOrderData = bufferArrayToHex(zeroExOrderBuffer); - }); - - it("works", async () => { - const result = await zeroExExchangeWrapper.parseZeroExOrder.callAsync( - subjectOrderData, - makerAssetDataLength, - takerAssetDataLength - ); - - const [addresses, uints, makerAssetDataResult, takerAssetDataResult] = result; - const [makerResult, takerResult, feeRecipientResult, senderResult] = addresses; - const [ - makerAssetAmountResult, - takerAssetAmountResult, - makerFeeResult, - takerFeeResult, - expirationResult, - saltResult, - ] = uints; - - expect(ownerAccount).to.equal(makerResult); - expect(takerAddress).to.equal(takerResult); - expect(feeRecipientAddress).to.equal(feeRecipientResult); - expect(senderAddress).to.equal(senderResult); - expect(makerAssetAmount).to.be.bignumber.equal(makerAssetAmountResult); - expect(takerAssetAmount).to.be.bignumber.equal(takerAssetAmountResult); - expect(makerFee).to.be.bignumber.equal(makerFeeResult); - expect(takerFee).to.be.bignumber.equal(takerFeeResult); - expect(expirationTimeSeconds).to.be.bignumber.equal(expirationResult); - expect(salt).to.be.bignumber.equal(saltResult); - expect(makerAssetData).to.equal(web3.toAscii(makerAssetDataResult)); - expect(takerAssetData).to.equal(web3.toAscii(takerAssetDataResult)); - }); - }); - describe("#parseZeroExOrderData", async () => { let subjectOrderData: Bytes32; diff --git a/test/core/external/zeroExExchangeWrapper.spec.ts b/test/core/external/zeroExExchangeWrapper.spec.ts index e49321aa6..b85d2838a 100644 --- a/test/core/external/zeroExExchangeWrapper.spec.ts +++ b/test/core/external/zeroExExchangeWrapper.spec.ts @@ -39,20 +39,6 @@ contract("ZeroExExchangeWrapper", (accounts) => { const [ownerAccount, takerAddress, feeRecipientAddress, senderAddress] = accounts; let zeroExExchangeWrapper: ZeroExExchangeWrapperContract; - - let signature: ZeroExSignature = "ABCDEFHIJKLMNOPQRSTUVWXYZ"; - let signatureLength: UInt = signature.length; - - let zeroExOrder: ZeroExOrder; - let zeroExOrderLength = 0; - - let fillAmount = 5; - - let makerAssetDataLength = 4; - let takerAssetDataLength = 3; - - - beforeEach(async () => { const zeroExExchangeWrapperInstance = await ZeroExExchangeWrapper.new( { from: ownerAccount, gas: DEFAULT_GAS }, From 2fb755795668aaf6d9d5e71836d03af1c5dff636 Mon Sep 17 00:00:00 2001 From: felix2feng Date: Tue, 3 Jul 2018 12:31:11 -0700 Subject: [PATCH 7/9] Do some refactors on the tests --- .../external/lib/ZeroExOrderDataHandler.sol | 2 +- package.json | 2 +- .../mockZeroExOrderDataHandlerLibrary.spec.ts | 90 ++++++++----------- .../external/zeroExExchangeWrapper.spec.ts | 5 -- test/utils/zeroExExchangeWrapper.ts | 83 ++++++++++++----- 5 files changed, 101 insertions(+), 81 deletions(-) diff --git a/contracts/core/external/lib/ZeroExOrderDataHandler.sol b/contracts/core/external/lib/ZeroExOrderDataHandler.sol index 560e9a579..0cb2622c4 100644 --- a/contracts/core/external/lib/ZeroExOrderDataHandler.sol +++ b/contracts/core/external/lib/ZeroExOrderDataHandler.sol @@ -152,7 +152,7 @@ library ZeroExOrderDataHandler { // | | 0x140 | | 11. Offset to makerAssetData (*) | // | | 0x160 | | 12. Offset to takerAssetData (*) | // | | 0x180 | 32 | makerAssetData Length | - NOT IMPLEMENTED - // | | 0x1A0 | ** | makerAssetData Contents | - NOT IMPLEMENTED + // | | 0x1A0 | ** | makerAssetData Contents | - NOT IMPLEMENTED` // | | 0x1C0 | 32 | takerAssetData Length | - NOT IMPLEMENTED // | | 0x1E0 | ** | takerAssetData Contents | - NOT IMPLEMENTED assembly { diff --git a/package.json b/package.json index 1f78b6268..0d97ae794 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "clean": "rm -rf build; rm -rf transpiled; rm -rf types/generated", "compile": "truffle compile", "prepare-test": "yarn clean && truffle compile --all && yarn run generate-typings && yarn run transpile", - "test": "yarn prepare-test && truffle test `find ./transpiled/test -name 'mockZeroExOrderDataHandlerLibrary.spec.js'`", + "test": "yarn prepare-test && truffle test `find ./transpiled/test -name '*.spec.js'`", "transpile": "tsc", "generate-typings": "abi-gen --abis './build/contracts/*.json' --out './types/generated' --template './types/contract_templates/contract.mustache' --partials './types/contract_templates/partials/*.mustache' && yarn run rename-generated-abi", "rename-generated-abi": "mv types/generated/detailed_e_r_c20.ts types/generated/detailed_erc20.ts && mv types/generated/e_r_c20_basic.ts types/generated/erc20_basic.ts", diff --git a/test/core/external/lib/mockZeroExOrderDataHandlerLibrary.spec.ts b/test/core/external/lib/mockZeroExOrderDataHandlerLibrary.spec.ts index 6316fb4f0..64e549369 100644 --- a/test/core/external/lib/mockZeroExOrderDataHandlerLibrary.spec.ts +++ b/test/core/external/lib/mockZeroExOrderDataHandlerLibrary.spec.ts @@ -16,12 +16,10 @@ import { MockZeroExOrderDataHandlerLibraryContract } from "../../../../types/gen const MockZeroExOrderDataHandlerLibrary = artifacts.require("MockZeroExOrderDataHandlerLibrary"); import { - bufferOrderHeader, - bufferFillAmount, - bufferSignature, bufferZeroExOrder, - bufferArrayToHex, createZeroExOrder, + getZeroExOrderLengthFromBuffer, + generateStandardZeroExOrderBytesArray, } from "../../../utils/zeroExExchangeWrapper"; // Testing Set up @@ -69,11 +67,6 @@ contract("MockZeroExOrderDataHandlerLibrary", (accounts) => { takerAssetData, ); - // Header Subject Data - let signatureLength: UInt = new BigNumber(signature.length); - let zeroExOrderLength = new BigNumber(0); - let makerAssetDataLength = new BigNumber(makerAssetData.length); - let takerAssetDataLength = new BigNumber(takerAssetData.length); beforeEach(async () => { const zeroExExchangeWrapperInstance = await MockZeroExOrderDataHandlerLibrary.new( @@ -87,18 +80,31 @@ contract("MockZeroExOrderDataHandlerLibrary", (accounts) => { }); describe("#parseOrderDataHeader", async () => { + // Header Subject Data + let signatureLength: UInt; + let zeroExOrderLength: BigNumber; + let makerAssetDataLength: BigNumber; + let takerAssetDataLength: BigNumber; + + let subjectOrderData: Bytes32; beforeEach(async () => { - subjectOrderData = bufferArrayToHex(bufferOrderHeader( - signatureLength, - zeroExOrderLength, - makerAssetDataLength, - takerAssetDataLength - )); + subjectOrderData = generateStandardZeroExOrderBytesArray( + zeroExOrder, + signature, + fillAmount, + ); + + const zeroExOrderBuffer = bufferZeroExOrder(zeroExOrder); + zeroExOrderLength = getZeroExOrderLengthFromBuffer(zeroExOrderBuffer); + + signatureLength = new BigNumber(signature.length); + makerAssetDataLength = new BigNumber(makerAssetData.length); + takerAssetDataLength = new BigNumber(takerAssetData.length); }); - it("works", async () => { + it("should correctly parse the order data header", async () => { const result = await zeroExExchangeWrapper.parseOrderDataHeader.callAsync(subjectOrderData); expect(result[0]).to.bignumber.equal(signatureLength); @@ -112,17 +118,14 @@ contract("MockZeroExOrderDataHandlerLibrary", (accounts) => { let subjectOrderData: Bytes32; beforeEach(async () => { - subjectOrderData = bufferArrayToHex( - bufferOrderHeader( - signatureLength, - zeroExOrderLength, - makerAssetDataLength, - takerAssetDataLength, - ).concat(bufferFillAmount(fillAmount) - )); + subjectOrderData = generateStandardZeroExOrderBytesArray( + zeroExOrder, + signature, + fillAmount, + ); }); - it("works", async () => { + it("correctly parse the fill amount", async () => { const result = await zeroExExchangeWrapper.parseFillAmount.callAsync(subjectOrderData); expect(result).to.be.bignumber.equal(fillAmount); }); @@ -132,19 +135,14 @@ contract("MockZeroExOrderDataHandlerLibrary", (accounts) => { let subjectOrderData: Bytes32; beforeEach(async () => { - subjectOrderData = bufferArrayToHex( - bufferOrderHeader( - signatureLength, - zeroExOrderLength, - makerAssetDataLength, - takerAssetDataLength, - ) - .concat(bufferFillAmount(fillAmount)) - .concat(bufferSignature(signature) - )); + subjectOrderData = generateStandardZeroExOrderBytesArray( + zeroExOrder, + signature, + fillAmount, + ); }); - it("works", async () => { + it("should correctly parse the signature", async () => { const result = await zeroExExchangeWrapper.parseSignature.callAsync(subjectOrderData); expect(web3.toAscii(result)).to.equal(signature); }); @@ -154,24 +152,14 @@ contract("MockZeroExOrderDataHandlerLibrary", (accounts) => { let subjectOrderData: Bytes32; beforeEach(async () => { - const zeroExOrderBuffer = bufferZeroExOrder(zeroExOrder); - - zeroExOrderLength = new BigNumber(Buffer.concat(zeroExOrderBuffer).length); - - subjectOrderData = bufferArrayToHex( - bufferOrderHeader( - signatureLength, - zeroExOrderLength, - makerAssetDataLength, - takerAssetDataLength, - ) - .concat(bufferFillAmount(fillAmount)) - .concat(bufferSignature(signature)) - .concat(zeroExOrderBuffer) + subjectOrderData = generateStandardZeroExOrderBytesArray( + zeroExOrder, + signature, + fillAmount, ); }); - it("works", async () => { + it("should correctly parse the zeroEx order", async () => { const result = await zeroExExchangeWrapper.parseZeroExOrderData.callAsync(subjectOrderData); const [addresses, uints, makerAssetDataResult, takerAssetDataResult] = result; diff --git a/test/core/external/zeroExExchangeWrapper.spec.ts b/test/core/external/zeroExExchangeWrapper.spec.ts index b85d2838a..2e6cb1212 100644 --- a/test/core/external/zeroExExchangeWrapper.spec.ts +++ b/test/core/external/zeroExExchangeWrapper.spec.ts @@ -16,11 +16,6 @@ import { ZeroExExchangeWrapperContract } from "../../../types/generated/zero_ex_ const ZeroExExchangeWrapper = artifacts.require("ZeroExExchangeWrapper"); import { - bufferOrderHeader, - bufferFillAmount, - bufferSignature, - bufferZeroExOrder, - bufferArrayToHex, createZeroExOrder, } from "../../utils/zeroExExchangeWrapper"; diff --git a/test/utils/zeroExExchangeWrapper.ts b/test/utils/zeroExExchangeWrapper.ts index 8684f3af2..786e4b5c3 100644 --- a/test/utils/zeroExExchangeWrapper.ts +++ b/test/utils/zeroExExchangeWrapper.ts @@ -5,7 +5,7 @@ const web3 = new Web3(); import { BigNumber } from "bignumber.js"; import { Address, Bytes32, Bytes, UInt } from "../../types/common.js"; -import { ZeroExOrder } from "../../types/zeroEx"; +import { ZeroExOrder, ZeroExSignature, ZeroExOrderHeader } from "../../types/zeroEx"; function bufferAndLPad32(input: any): Buffer { return ethUtil.setLengthLeft(ethUtil.toBuffer(input), 32); @@ -42,30 +42,41 @@ export function createZeroExOrder( } } -export function bufferOrderHeader( - signatureLength: UInt, - orderLength: UInt, - makerAssetDataLength: UInt, - takerAssetDataLength: UInt, -): Buffer[] { - return [ - bufferAndLPad32(web3.toHex(signatureLength)), - bufferAndLPad32(web3.toHex(orderLength)), - bufferAndLPad32(web3.toHex(makerAssetDataLength)), - bufferAndLPad32(web3.toHex(takerAssetDataLength)), - ] -} +export function generateStandardZeroExOrderBytesArray( + zeroExOrder: ZeroExOrder, + signature: ZeroExSignature, + fillAmount: UInt, +) { + const { makerAssetData, takerAssetData } = zeroExOrder; -export function bufferFillAmount( - fillAmount: UInt = 0, -): Buffer[] { - return [bufferAndLPad32(web3.toHex(fillAmount))]; + const makerAssetDataLength = new BigNumber(makerAssetData.length); + const takerAssetDataLength = new BigNumber(takerAssetData.length); + + // Get signature length + const signatureLength: UInt = new BigNumber(signature.length); + + // Get order length + const zeroExOrderBuffer = bufferZeroExOrder(zeroExOrder); + const zeroExOrderLength = getZeroExOrderLengthFromBuffer(zeroExOrderBuffer); + + // Generate the standard byte array + return bufferArrayToHex( + bufferOrderHeader( + signatureLength, + zeroExOrderLength, + makerAssetDataLength, + takerAssetDataLength, + ) + .concat(bufferFillAmount(fillAmount)) + .concat(bufferSignature(signature)) + .concat(zeroExOrderBuffer) + ); } -export function bufferSignature( - signature: Bytes32 = '', -): Buffer[] { - return [ethUtil.toBuffer(signature)]; +export function getZeroExOrderLengthFromBuffer( + zeroExOrder: Buffer[], +): BigNumber { + return new BigNumber(Buffer.concat(zeroExOrder).length); } export function bufferZeroExOrder( @@ -87,7 +98,33 @@ export function bufferZeroExOrder( ]; } -export function bufferArrayToHex( +function bufferOrderHeader( + signatureLength: UInt, + orderLength: UInt, + makerAssetDataLength: UInt, + takerAssetDataLength: UInt, +): Buffer[] { + return [ + bufferAndLPad32(web3.toHex(signatureLength)), + bufferAndLPad32(web3.toHex(orderLength)), + bufferAndLPad32(web3.toHex(makerAssetDataLength)), + bufferAndLPad32(web3.toHex(takerAssetDataLength)), + ] +} + +function bufferFillAmount( + fillAmount: UInt = 0, +): Buffer[] { + return [bufferAndLPad32(web3.toHex(fillAmount))]; +} + +function bufferSignature( + signature: Bytes32 = '', +): Buffer[] { + return [ethUtil.toBuffer(signature)]; +} + +function bufferArrayToHex( bufferArr: Buffer[] ): Bytes32 { const buffer = Buffer.concat(bufferArr); From 5cb1b9a4fcdac4a3d2d90ad386300d2a4984e74b Mon Sep 17 00:00:00 2001 From: felix2feng Date: Tue, 3 Jul 2018 12:44:11 -0700 Subject: [PATCH 8/9] Fix coverage --- contracts/core/external/ZeroExExchangeWrapper.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/core/external/ZeroExExchangeWrapper.sol b/contracts/core/external/ZeroExExchangeWrapper.sol index 828a9e2c1..cfffe48f6 100644 --- a/contracts/core/external/ZeroExExchangeWrapper.sol +++ b/contracts/core/external/ZeroExExchangeWrapper.sol @@ -57,7 +57,7 @@ contract ZeroExExchangeWrapper bytes orderData ) external - returns (uint256) + // returns (uint256) { // Parse fill Amount @@ -75,7 +75,7 @@ contract ZeroExExchangeWrapper // Ensure allowance - return 1; + // return 1; } /* ============ Getters ============ */ From a79c9fb02aeaacf3bb10cd9d4399192fd21e4b1e Mon Sep 17 00:00:00 2001 From: felix2feng Date: Tue, 3 Jul 2018 13:52:25 -0700 Subject: [PATCH 9/9] Fix some linting --- .soliumrc.json | 3 +- .../core/external/ZeroExExchangeWrapper.sol | 7 +- .../external/lib/ZeroExOrderDataHandler.sol | 76 ++++++++++--------- .../test/lib/ZeroExOrderDataHandlerLib.sol | 2 +- .../mockZeroExOrderDataHandlerLibrary.spec.ts | 38 +++++++--- test/utils/zeroExExchangeWrapper.ts | 3 +- 6 files changed, 74 insertions(+), 55 deletions(-) diff --git a/.soliumrc.json b/.soliumrc.json index e6ddaf36c..c3d5711fa 100644 --- a/.soliumrc.json +++ b/.soliumrc.json @@ -14,6 +14,7 @@ "security/no-block-members": 0, "security/no-named-returns": ["error"], "security/no-suicide-or-selfdestruct": ["error"], - "security/no-var": ["error"] + "security/no-var": ["error"], + "security/no-inline-assembly": "off" } } diff --git a/contracts/core/external/ZeroExExchangeWrapper.sol b/contracts/core/external/ZeroExExchangeWrapper.sol index cfffe48f6..904949606 100644 --- a/contracts/core/external/ZeroExExchangeWrapper.sol +++ b/contracts/core/external/ZeroExExchangeWrapper.sol @@ -21,6 +21,7 @@ import { SafeMath } from "zeppelin-solidity/contracts/math/SafeMath.sol"; import { LibBytes } from "../../external/LibBytes.sol"; import { ZeroExOrderDataHandler as ZeroEx } from "./lib/ZeroExOrderDataHandler.sol"; + /** * @title ZeroExExchangeWrapper * @author Set Protocol @@ -38,7 +39,7 @@ contract ZeroExExchangeWrapper /* ============ Constructor ============ */ - + constructor( // address _zeroExExchange, // address _zeroExProxy, @@ -80,9 +81,5 @@ contract ZeroExExchangeWrapper /* ============ Getters ============ */ - - - /* ============ Private ============ */ - } diff --git a/contracts/core/external/lib/ZeroExOrderDataHandler.sol b/contracts/core/external/lib/ZeroExOrderDataHandler.sol index 0cb2622c4..ad08379f9 100644 --- a/contracts/core/external/lib/ZeroExOrderDataHandler.sol +++ b/contracts/core/external/lib/ZeroExOrderDataHandler.sol @@ -35,17 +35,17 @@ library ZeroExOrderDataHandler { struct Order { address makerAddress; // Address that created the order. - address takerAddress; // Address that is allowed to fill the order. If set to 0, any address is allowed to fill the order. + address takerAddress; // Address that is allowed to fill the order. address feeRecipientAddress; // Address that will recieve fees when order is filled. - address senderAddress; // Address that is allowed to call Exchange contract methods that affect this order. If set to 0, any address is allowed to call these methods. - uint256 makerAssetAmount; // Amount of makerAsset being offered by maker. Must be greater than 0. - uint256 takerAssetAmount; // Amount of takerAsset being bid on by maker. Must be greater than 0. - uint256 makerFee; // Amount of ZRX paid to feeRecipient by maker when order is filled. If set to 0, no transfer of ZRX from maker to feeRecipient will be attempted. - uint256 takerFee; // Amount of ZRX paid to feeRecipient by taker when order is filled. If set to 0, no transfer of ZRX from taker to feeRecipient will be attempted. + address senderAddress; // Address that is allowed to call Exchange contract. + uint256 makerAssetAmount; // Amount of makerAsset being offered by maker. + uint256 takerAssetAmount; // Amount of takerAsset being bid on by maker. + uint256 makerFee; // Amount of ZRX paid to feeRecipient by maker + uint256 takerFee; // Amount of ZRX paid to feeRecipient by taker uint256 expirationTimeSeconds; // Timestamp in seconds at which order expires. - uint256 salt; // Arbitrary number to facilitate uniqueness of the order's hash. - bytes makerAssetData; // Encoded data that can be decoded by a specified proxy contract when transferring makerAsset. The last byte references the id of this proxy. - bytes takerAssetData; // Encoded data that can be decoded by a specified proxy contract when transferring takerAsset. The last byte references the id of this proxy. + uint256 salt; // Number to facilitate uniqueness of the order's hash. + bytes makerAssetData; // Encoded data when transferring makerAsset. + bytes takerAssetData; // Encoded data when transferring takerAsset. } struct ZeroExHeader { @@ -99,13 +99,16 @@ library ZeroExOrderDataHandler { function parseFillAmount(bytes _orderData) internal pure - returns (uint256 fillAmount) + returns (uint256) { uint256 orderDataAddr = _orderData.contentAddress(); + uint256 fillAmount; assembly { fillAmount := mload(add(orderDataAddr, 128)) } + + return fillAmount; } function sliceSignature(bytes _orderData, uint _signatureLength) @@ -124,37 +127,41 @@ library ZeroExOrderDataHandler { { uint256 orderDataAddr = _orderData.contentAddress(); uint256 orderStartAddress = orderDataAddr.add(_signatureLength); - bytes memory order = _orderData.slice(orderStartAddress, orderStartAddress.add(_orderLength)); + bytes memory order = _orderData.slice( + orderStartAddress, + orderStartAddress.add(_orderLength) + ); return order; } - function parseZeroExOrder(bytes _zeroExOrder, uint _makerAssetDataLength, uint _takerAssetDataLength) + function parseZeroExOrder( + bytes _zeroExOrder, + uint _makerAssetDataLength, + uint _takerAssetDataLength + ) internal pure returns (Order memory) { - Order memory order; uint256 orderDataAddr = _zeroExOrder.contentAddress(); - - // | 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 | - NOT IMPLEMENTED - // | | 0x1A0 | ** | makerAssetData Contents | - NOT IMPLEMENTED` - // | | 0x1C0 | 32 | takerAssetData Length | - NOT IMPLEMENTED - // | | 0x1E0 | ** | takerAssetData Contents | - NOT IMPLEMENTED + // | Data | Location | Length | + // |----------------------------|----------|--------| + // | maker | 0 | | + // | taker | 32 | | + // | feeRecipient | 64 | | + // | senderAddress | 96 | | + // | makerAssetAmount | 128 | | + // | takerAssetAmount | 160 | | + // | makerFee | 192 | | + // | takerFee | 224 | | + // | expirationUnixTimeStampSec | 256 | | + // | salt | 288 | | + // | makerAssetData | 320 | ** | + // | takerAssetData | 320 + ** | *** | + // ** - Maker Asset Data Length + // *** - Taker Asset Data Length assembly { mstore(order, mload(orderDataAddr)) // maker mstore(add(order, 32), mload(add(orderDataAddr, 32))) // taker @@ -169,7 +176,10 @@ library ZeroExOrderDataHandler { } order.makerAssetData = _zeroExOrder.slice(320, _makerAssetDataLength.add(320)); - order.takerAssetData = _zeroExOrder.slice(_makerAssetDataLength.add(320), _makerAssetDataLength.add(320).add(_takerAssetDataLength)); + order.takerAssetData = _zeroExOrder.slice( + _makerAssetDataLength.add(320), + _makerAssetDataLength.add(320).add(_takerAssetDataLength) + ); return order; } @@ -181,8 +191,6 @@ library ZeroExOrderDataHandler { { ZeroExHeader memory header = parseOrderHeader(_orderData); - uint fillAmount = parseFillAmount(_orderData); - bytes memory signature = sliceSignature(_orderData, header.signatureLength); Order memory order = parseZeroExOrder( sliceZeroExOrder(_orderData, header.signatureLength, header.orderLength), header.makerAssetDataLength, diff --git a/contracts/test/lib/ZeroExOrderDataHandlerLib.sol b/contracts/test/lib/ZeroExOrderDataHandlerLib.sol index 7bf64e2a9..8b4f74d71 100644 --- a/contracts/test/lib/ZeroExOrderDataHandlerLib.sol +++ b/contracts/test/lib/ZeroExOrderDataHandlerLib.sol @@ -63,5 +63,5 @@ contract MockZeroExOrderDataHandlerLibrary { order.makerAssetData, order.takerAssetData ); - } + } } diff --git a/test/core/external/lib/mockZeroExOrderDataHandlerLibrary.spec.ts b/test/core/external/lib/mockZeroExOrderDataHandlerLibrary.spec.ts index 64e549369..0f7c441e3 100644 --- a/test/core/external/lib/mockZeroExOrderDataHandlerLibrary.spec.ts +++ b/test/core/external/lib/mockZeroExOrderDataHandlerLibrary.spec.ts @@ -67,7 +67,6 @@ contract("MockZeroExOrderDataHandlerLibrary", (accounts) => { takerAssetData, ); - beforeEach(async () => { const zeroExExchangeWrapperInstance = await MockZeroExOrderDataHandlerLibrary.new( { from: ownerAccount, gas: DEFAULT_GAS }, @@ -104,13 +103,17 @@ contract("MockZeroExOrderDataHandlerLibrary", (accounts) => { takerAssetDataLength = new BigNumber(takerAssetData.length); }); + async function subject(): Promise { + return zeroExExchangeWrapper.parseOrderDataHeader.callAsync(subjectOrderData); + } + it("should correctly parse the order data header", async () => { - const result = await zeroExExchangeWrapper.parseOrderDataHeader.callAsync(subjectOrderData); + const [sigLen, zeroExOrderLen, makerAssetDataLen, takerAssetDataLen ] = await subject(); - expect(result[0]).to.bignumber.equal(signatureLength); - expect(result[1]).to.bignumber.equal(zeroExOrderLength); - expect(result[2]).to.bignumber.equal(makerAssetDataLength); - expect(result[3]).to.bignumber.equal(takerAssetDataLength); + expect(sigLen).to.bignumber.equal(signatureLength); + expect(zeroExOrderLen).to.bignumber.equal(zeroExOrderLength); + expect(makerAssetDataLen).to.bignumber.equal(makerAssetDataLength); + expect(takerAssetDataLen).to.bignumber.equal(takerAssetDataLength); }); }); @@ -125,9 +128,13 @@ contract("MockZeroExOrderDataHandlerLibrary", (accounts) => { ); }); + async function subject(): Promise { + return zeroExExchangeWrapper.parseFillAmount.callAsync(subjectOrderData); + } + it("correctly parse the fill amount", async () => { - const result = await zeroExExchangeWrapper.parseFillAmount.callAsync(subjectOrderData); - expect(result).to.be.bignumber.equal(fillAmount); + const fillAmountResult = await subject(); + expect(fillAmountResult).to.be.bignumber.equal(fillAmount); }); }); @@ -142,9 +149,13 @@ contract("MockZeroExOrderDataHandlerLibrary", (accounts) => { ); }); + async function subject(): Promise { + return zeroExExchangeWrapper.parseSignature.callAsync(subjectOrderData); + } + it("should correctly parse the signature", async () => { - const result = await zeroExExchangeWrapper.parseSignature.callAsync(subjectOrderData); - expect(web3.toAscii(result)).to.equal(signature); + const signatureResult = await subject(); + expect(web3.toAscii(signatureResult)).to.equal(signature); }); }); @@ -159,10 +170,13 @@ contract("MockZeroExOrderDataHandlerLibrary", (accounts) => { ); }); + async function subject(): Promise { + return zeroExExchangeWrapper.parseZeroExOrderData.callAsync(subjectOrderData); + } + it("should correctly parse the zeroEx order", async () => { - const result = await zeroExExchangeWrapper.parseZeroExOrderData.callAsync(subjectOrderData); + const [addresses, uints, makerAssetDataResult, takerAssetDataResult] = await subject(); - const [addresses, uints, makerAssetDataResult, takerAssetDataResult] = result; const [makerResult, takerResult, feeRecipientResult, senderResult] = addresses; const [ makerAssetAmountResult, diff --git a/test/utils/zeroExExchangeWrapper.ts b/test/utils/zeroExExchangeWrapper.ts index 786e4b5c3..a47afc285 100644 --- a/test/utils/zeroExExchangeWrapper.ts +++ b/test/utils/zeroExExchangeWrapper.ts @@ -11,7 +11,6 @@ function bufferAndLPad32(input: any): Buffer { return ethUtil.setLengthLeft(ethUtil.toBuffer(input), 32); } - export function createZeroExOrder( makerAddress: Address, takerAddress: Address, @@ -109,7 +108,7 @@ function bufferOrderHeader( bufferAndLPad32(web3.toHex(orderLength)), bufferAndLPad32(web3.toHex(makerAssetDataLength)), bufferAndLPad32(web3.toHex(takerAssetDataLength)), - ] + ]; } function bufferFillAmount(