aboutsummaryrefslogtreecommitdiffstats
path: root/contracts/libs
diff options
context:
space:
mode:
Diffstat (limited to 'contracts/libs')
-rw-r--r--contracts/libs/.solhint.json20
-rw-r--r--contracts/libs/README.md70
-rw-r--r--contracts/libs/compiler.json22
-rw-r--r--contracts/libs/contracts/libs/LibAbiEncoder.sol215
-rw-r--r--contracts/libs/contracts/libs/LibAssetProxyErrors.sol38
-rw-r--r--contracts/libs/contracts/libs/LibConstants.sol49
-rw-r--r--contracts/libs/contracts/libs/LibEIP712.sol87
-rw-r--r--contracts/libs/contracts/libs/LibExchangeErrors.sol70
-rw-r--r--contracts/libs/contracts/libs/LibFillResults.sol53
-rw-r--r--contracts/libs/contracts/libs/LibMath.sol253
-rw-r--r--contracts/libs/contracts/libs/LibOrder.sol145
-rw-r--r--contracts/libs/contracts/test/TestLibs/TestLibs.sol152
-rw-r--r--contracts/libs/package.json92
-rw-r--r--contracts/libs/src/artifacts/index.ts17
-rw-r--r--contracts/libs/src/index.ts2
-rw-r--r--contracts/libs/src/wrappers/index.ts6
-rw-r--r--contracts/libs/test/exchange/libs.ts125
-rw-r--r--contracts/libs/test/global_hooks.ts17
-rw-r--r--contracts/libs/tsconfig.json18
-rw-r--r--contracts/libs/tslint.json6
20 files changed, 1457 insertions, 0 deletions
diff --git a/contracts/libs/.solhint.json b/contracts/libs/.solhint.json
new file mode 100644
index 000000000..076afe9f3
--- /dev/null
+++ b/contracts/libs/.solhint.json
@@ -0,0 +1,20 @@
+{
+ "extends": "default",
+ "rules": {
+ "avoid-low-level-calls": false,
+ "avoid-tx-origin": "warn",
+ "bracket-align": false,
+ "code-complexity": false,
+ "const-name-snakecase": "error",
+ "expression-indent": "error",
+ "function-max-lines": false,
+ "func-order": "error",
+ "indent": ["error", 4],
+ "max-line-length": ["warn", 160],
+ "no-inline-assembly": false,
+ "quotes": ["error", "double"],
+ "separate-by-one-line-in-contract": "error",
+ "space-after-comma": "error",
+ "statement-indent": "error"
+ }
+}
diff --git a/contracts/libs/README.md b/contracts/libs/README.md
new file mode 100644
index 000000000..66eedf6be
--- /dev/null
+++ b/contracts/libs/README.md
@@ -0,0 +1,70 @@
+## Contracts libs
+
+Smart contracts libs used in the 0x protocol.
+
+## Usage
+
+Contracts can be found in the [contracts](./contracts) directory. The contents of this directory are broken down into the following subdirectories:
+
+* [libs](./contracts/protocol)
+ * This directory contains the libs.
+* [test](./contracts/test)
+ * This directory contains mocks and other contracts that are used solely for testing contracts within the other directories.
+
+## Contributing
+
+We strongly recommend that the community help us make improvements and determine the future direction of the protocol. To report bugs within this package, please create an issue in this repository.
+
+For proposals regarding the 0x protocol's smart contract architecture, message format, or additional functionality, go to the [0x Improvement Proposals (ZEIPs)](https://github.com/0xProject/ZEIPs) repository and follow the contribution guidelines provided therein.
+
+Please read our [contribution guidelines](../../CONTRIBUTING.md) before getting started.
+
+### Install Dependencies
+
+If you don't have yarn workspaces enabled (Yarn < v1.0) - enable them:
+
+```bash
+yarn config set workspaces-experimental true
+```
+
+Then install dependencies
+
+```bash
+yarn install
+```
+
+### Build
+
+To build this package and all other monorepo packages that it depends on, run the following from the monorepo root directory:
+
+```bash
+PKG=@0x/contracts-libs yarn build
+```
+
+Or continuously rebuild on change:
+
+```bash
+PKG=@0x/contracts-libs yarn watch
+```
+
+### Clean
+
+```bash
+yarn clean
+```
+
+### Lint
+
+```bash
+yarn lint
+```
+
+### Run Tests
+
+```bash
+yarn test
+```
+
+#### Testing options
+
+Contracts testing options like coverage, profiling, revert traces or backing node choosing - are described [here](../TESTING.md).
diff --git a/contracts/libs/compiler.json b/contracts/libs/compiler.json
new file mode 100644
index 000000000..cf7c52a73
--- /dev/null
+++ b/contracts/libs/compiler.json
@@ -0,0 +1,22 @@
+{
+ "artifactsDir": "./generated-artifacts",
+ "contractsDir": "./contracts",
+ "compilerSettings": {
+ "optimizer": {
+ "enabled": true,
+ "runs": 1000000
+ },
+ "outputSelection": {
+ "*": {
+ "*": [
+ "abi",
+ "evm.bytecode.object",
+ "evm.bytecode.sourceMap",
+ "evm.deployedBytecode.object",
+ "evm.deployedBytecode.sourceMap"
+ ]
+ }
+ }
+ },
+ "contracts": ["TestLibs", "LibOrder", "LibMath", "LibFillResults", "LibAbiEncoder", "LibEIP712"]
+}
diff --git a/contracts/libs/contracts/libs/LibAbiEncoder.sol b/contracts/libs/contracts/libs/LibAbiEncoder.sol
new file mode 100644
index 000000000..4aad37709
--- /dev/null
+++ b/contracts/libs/contracts/libs/LibAbiEncoder.sol
@@ -0,0 +1,215 @@
+/*
+
+ Copyright 2018 ZeroEx Intl.
+
+ 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 "./LibOrder.sol";
+
+
+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;
+ }
+}
diff --git a/contracts/libs/contracts/libs/LibAssetProxyErrors.sol b/contracts/libs/contracts/libs/LibAssetProxyErrors.sol
new file mode 100644
index 000000000..1d9a70cc1
--- /dev/null
+++ b/contracts/libs/contracts/libs/LibAssetProxyErrors.sol
@@ -0,0 +1,38 @@
+/*
+
+ Copyright 2018 ZeroEx Intl.
+
+ 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.
+
+*/
+
+// solhint-disable
+pragma solidity 0.4.24;
+
+
+/// @dev This contract documents the revert reasons used in the AssetProxy contracts.
+/// This contract is intended to serve as a reference, but is not actually used for efficiency reasons.
+contract LibAssetProxyErrors {
+
+ /// Authorizable errors ///
+ string constant SENDER_NOT_AUTHORIZED = "SENDER_NOT_AUTHORIZED"; // Sender not authorized to call this method.
+ string constant TARGET_NOT_AUTHORIZED = "TARGET_NOT_AUTHORIZED"; // Target address not authorized to call this method.
+ string constant TARGET_ALREADY_AUTHORIZED = "TARGET_ALREADY_AUTHORIZED"; // Target address must not already be authorized.
+ string constant INDEX_OUT_OF_BOUNDS = "INDEX_OUT_OF_BOUNDS"; // Specified array index is out of bounds.
+ string constant AUTHORIZED_ADDRESS_MISMATCH = "AUTHORIZED_ADDRESS_MISMATCH"; // Address at index does not match given target address.
+
+ /// Transfer errors ///
+ string constant INVALID_AMOUNT = "INVALID_AMOUNT"; // Transfer amount must equal 1.
+ string constant TRANSFER_FAILED = "TRANSFER_FAILED"; // Transfer failed.
+ string constant LENGTH_GREATER_THAN_131_REQUIRED = "LENGTH_GREATER_THAN_131_REQUIRED"; // Byte array must have a length greater than 0.
+}
diff --git a/contracts/libs/contracts/libs/LibConstants.sol b/contracts/libs/contracts/libs/LibConstants.sol
new file mode 100644
index 000000000..8d2732cd3
--- /dev/null
+++ b/contracts/libs/contracts/libs/LibConstants.sol
@@ -0,0 +1,49 @@
+/*
+
+ Copyright 2018 ZeroEx Intl.
+
+ 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;
+
+
+// solhint-disable max-line-length
+contract LibConstants {
+
+ // Asset data for ZRX token. Used for fee transfers.
+ // @TODO: Hardcode constant when we deploy. Currently
+ // not constant to make testing easier.
+
+ // The proxyId for ZRX_ASSET_DATA is bytes4(keccak256("ERC20Token(address)")) = 0xf47261b0
+
+ // Kovan ZRX address is 0x6ff6c0ff1d68b964901f986d4c9fa3ac68346570.
+ // The ABI encoded proxyId and address is 0xf47261b00000000000000000000000006ff6c0ff1d68b964901f986d4c9fa3ac68346570
+ // bytes constant public ZRX_ASSET_DATA = "\xf4\x72\x61\xb0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x6f\xf6\xc0\xff\x1d\x68\xb9\x64\x90\x1f\x98\x6d\x4c\x9f\xa3\xac\x68\x34\x65\x70";
+
+ // Mainnet ZRX address is 0xe41d2489571d322189246dafa5ebde1f4699f498.
+ // The ABI encoded proxyId and address is 0xf47261b0000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498
+ // bytes constant public ZRX_ASSET_DATA = "\xf4\x72\x61\xb0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe4\x1d\x24\x89\x57\x1d\x32\x21\x89\x24\x6d\xaf\xa5\xeb\xde\x1f\x46\x99\xf4\x98";
+
+ // solhint-disable-next-line var-name-mixedcase
+ bytes public ZRX_ASSET_DATA;
+
+ // @TODO: Remove when we deploy.
+ constructor (bytes memory zrxAssetData)
+ public
+ {
+ ZRX_ASSET_DATA = zrxAssetData;
+ }
+}
+// solhint-enable max-line-length
diff --git a/contracts/libs/contracts/libs/LibEIP712.sol b/contracts/libs/contracts/libs/LibEIP712.sol
new file mode 100644
index 000000000..203edc1fd
--- /dev/null
+++ b/contracts/libs/contracts/libs/LibEIP712.sol
@@ -0,0 +1,87 @@
+/*
+
+ Copyright 2018 ZeroEx Intl.
+
+ 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;
+
+
+contract LibEIP712 {
+
+ // EIP191 header for EIP712 prefix
+ string constant internal EIP191_HEADER = "\x19\x01";
+
+ // EIP712 Domain Name value
+ string constant internal EIP712_DOMAIN_NAME = "0x Protocol";
+
+ // EIP712 Domain Version value
+ string constant internal EIP712_DOMAIN_VERSION = "2";
+
+ // Hash of the EIP712 Domain Separator Schema
+ bytes32 constant internal EIP712_DOMAIN_SEPARATOR_SCHEMA_HASH = keccak256(abi.encodePacked(
+ "EIP712Domain(",
+ "string name,",
+ "string version,",
+ "address verifyingContract",
+ ")"
+ ));
+
+ // Hash of the EIP712 Domain Separator data
+ // solhint-disable-next-line var-name-mixedcase
+ bytes32 public EIP712_DOMAIN_HASH;
+
+ constructor ()
+ public
+ {
+ EIP712_DOMAIN_HASH = keccak256(abi.encodePacked(
+ EIP712_DOMAIN_SEPARATOR_SCHEMA_HASH,
+ keccak256(bytes(EIP712_DOMAIN_NAME)),
+ keccak256(bytes(EIP712_DOMAIN_VERSION)),
+ bytes32(address(this))
+ ));
+ }
+
+ /// @dev Calculates EIP712 encoding for a hash struct in this EIP712 Domain.
+ /// @param hashStruct The EIP712 hash struct.
+ /// @return EIP712 hash applied to this EIP712 Domain.
+ function hashEIP712Message(bytes32 hashStruct)
+ internal
+ view
+ returns (bytes32 result)
+ {
+ bytes32 eip712DomainHash = EIP712_DOMAIN_HASH;
+
+ // Assembly for more efficient computing:
+ // keccak256(abi.encodePacked(
+ // EIP191_HEADER,
+ // EIP712_DOMAIN_HASH,
+ // hashStruct
+ // ));
+
+ assembly {
+ // Load free memory pointer
+ let memPtr := mload(64)
+
+ mstore(memPtr, 0x1901000000000000000000000000000000000000000000000000000000000000) // EIP191 header
+ mstore(add(memPtr, 2), eip712DomainHash) // EIP712 domain hash
+ mstore(add(memPtr, 34), hashStruct) // Hash of struct
+
+ // Compute hash
+ result := keccak256(memPtr, 66)
+ }
+ return result;
+ }
+}
diff --git a/contracts/libs/contracts/libs/LibExchangeErrors.sol b/contracts/libs/contracts/libs/LibExchangeErrors.sol
new file mode 100644
index 000000000..a0f75bc06
--- /dev/null
+++ b/contracts/libs/contracts/libs/LibExchangeErrors.sol
@@ -0,0 +1,70 @@
+/*
+
+ Copyright 2018 ZeroEx Intl.
+
+ 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.
+
+*/
+
+// solhint-disable
+pragma solidity 0.4.24;
+
+
+/// @dev This contract documents the revert reasons used in the Exchange contract.
+/// This contract is intended to serve as a reference, but is not actually used for efficiency reasons.
+contract LibExchangeErrors {
+
+ /// Order validation errors ///
+ string constant ORDER_UNFILLABLE = "ORDER_UNFILLABLE"; // Order cannot be filled.
+ string constant INVALID_MAKER = "INVALID_MAKER"; // Invalid makerAddress.
+ string constant INVALID_TAKER = "INVALID_TAKER"; // Invalid takerAddress.
+ string constant INVALID_SENDER = "INVALID_SENDER"; // Invalid `msg.sender`.
+ string constant INVALID_ORDER_SIGNATURE = "INVALID_ORDER_SIGNATURE"; // Signature validation failed.
+
+ /// fillOrder validation errors ///
+ string constant INVALID_TAKER_AMOUNT = "INVALID_TAKER_AMOUNT"; // takerAssetFillAmount cannot equal 0.
+ string constant ROUNDING_ERROR = "ROUNDING_ERROR"; // Rounding error greater than 0.1% of takerAssetFillAmount.
+
+ /// Signature validation errors ///
+ string constant INVALID_SIGNATURE = "INVALID_SIGNATURE"; // Signature validation failed.
+ string constant SIGNATURE_ILLEGAL = "SIGNATURE_ILLEGAL"; // Signature type is illegal.
+ string constant SIGNATURE_UNSUPPORTED = "SIGNATURE_UNSUPPORTED"; // Signature type unsupported.
+
+ /// cancelOrdersUptTo errors ///
+ string constant INVALID_NEW_ORDER_EPOCH = "INVALID_NEW_ORDER_EPOCH"; // Specified salt must be greater than or equal to existing orderEpoch.
+
+ /// fillOrKillOrder errors ///
+ string constant COMPLETE_FILL_FAILED = "COMPLETE_FILL_FAILED"; // Desired takerAssetFillAmount could not be completely filled.
+
+ /// matchOrders errors ///
+ string constant NEGATIVE_SPREAD_REQUIRED = "NEGATIVE_SPREAD_REQUIRED"; // Matched orders must have a negative spread.
+
+ /// Transaction errors ///
+ string constant REENTRANCY_ILLEGAL = "REENTRANCY_ILLEGAL"; // Recursive reentrancy is not allowed.
+ string constant INVALID_TX_HASH = "INVALID_TX_HASH"; // Transaction has already been executed.
+ string constant INVALID_TX_SIGNATURE = "INVALID_TX_SIGNATURE"; // Signature validation failed.
+ string constant FAILED_EXECUTION = "FAILED_EXECUTION"; // Transaction execution failed.
+
+ /// registerAssetProxy errors ///
+ string constant ASSET_PROXY_ALREADY_EXISTS = "ASSET_PROXY_ALREADY_EXISTS"; // AssetProxy with same id already exists.
+
+ /// dispatchTransferFrom errors ///
+ string constant ASSET_PROXY_DOES_NOT_EXIST = "ASSET_PROXY_DOES_NOT_EXIST"; // No assetProxy registered at given id.
+ string constant TRANSFER_FAILED = "TRANSFER_FAILED"; // Asset transfer unsuccesful.
+
+ /// Length validation errors ///
+ string constant LENGTH_GREATER_THAN_0_REQUIRED = "LENGTH_GREATER_THAN_0_REQUIRED"; // Byte array must have a length greater than 0.
+ string constant LENGTH_GREATER_THAN_3_REQUIRED = "LENGTH_GREATER_THAN_3_REQUIRED"; // Byte array must have a length greater than 3.
+ string constant LENGTH_0_REQUIRED = "LENGTH_0_REQUIRED"; // Byte array must have a length of 0.
+ string constant LENGTH_65_REQUIRED = "LENGTH_65_REQUIRED"; // Byte array must have a length of 65.
+}
diff --git a/contracts/libs/contracts/libs/LibFillResults.sol b/contracts/libs/contracts/libs/LibFillResults.sol
new file mode 100644
index 000000000..fbd9950bf
--- /dev/null
+++ b/contracts/libs/contracts/libs/LibFillResults.sol
@@ -0,0 +1,53 @@
+/*
+
+ Copyright 2018 ZeroEx Intl.
+
+ 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;
+
+import "@0x/contracts-utils/contracts/utils/SafeMath/SafeMath.sol";
+
+
+contract LibFillResults is
+ SafeMath
+{
+ struct FillResults {
+ uint256 makerAssetFilledAmount; // Total amount of makerAsset(s) filled.
+ uint256 takerAssetFilledAmount; // Total amount of takerAsset(s) filled.
+ uint256 makerFeePaid; // Total amount of ZRX paid by maker(s) to feeRecipient(s).
+ uint256 takerFeePaid; // Total amount of ZRX paid by taker to feeRecipients(s).
+ }
+
+ struct MatchedFillResults {
+ FillResults left; // Amounts filled and fees paid of left order.
+ FillResults right; // Amounts filled and fees paid of right order.
+ uint256 leftMakerAssetSpreadAmount; // Spread between price of left and right order, denominated in the left order's makerAsset, paid to taker.
+ }
+
+ /// @dev Adds properties of both FillResults instances.
+ /// Modifies the first FillResults instance specified.
+ /// @param totalFillResults Fill results instance that will be added onto.
+ /// @param singleFillResults Fill results instance that will be added to totalFillResults.
+ function addFillResults(FillResults memory totalFillResults, FillResults memory singleFillResults)
+ internal
+ pure
+ {
+ totalFillResults.makerAssetFilledAmount = safeAdd(totalFillResults.makerAssetFilledAmount, singleFillResults.makerAssetFilledAmount);
+ totalFillResults.takerAssetFilledAmount = safeAdd(totalFillResults.takerAssetFilledAmount, singleFillResults.takerAssetFilledAmount);
+ totalFillResults.makerFeePaid = safeAdd(totalFillResults.makerFeePaid, singleFillResults.makerFeePaid);
+ totalFillResults.takerFeePaid = safeAdd(totalFillResults.takerFeePaid, singleFillResults.takerFeePaid);
+ }
+}
diff --git a/contracts/libs/contracts/libs/LibMath.sol b/contracts/libs/contracts/libs/LibMath.sol
new file mode 100644
index 000000000..b24876a9c
--- /dev/null
+++ b/contracts/libs/contracts/libs/LibMath.sol
@@ -0,0 +1,253 @@
+/*
+
+ Copyright 2018 ZeroEx Intl.
+
+ 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;
+
+import "@0x/contracts-utils/contracts/utils/SafeMath/SafeMath.sol";
+
+
+contract LibMath is
+ SafeMath
+{
+ /// @dev Calculates partial value given a numerator and denominator rounded down.
+ /// Reverts if rounding error is >= 0.1%
+ /// @param numerator Numerator.
+ /// @param denominator Denominator.
+ /// @param target Value to calculate partial of.
+ /// @return Partial value of target rounded down.
+ function safeGetPartialAmountFloor(
+ uint256 numerator,
+ uint256 denominator,
+ uint256 target
+ )
+ internal
+ pure
+ returns (uint256 partialAmount)
+ {
+ require(
+ denominator > 0,
+ "DIVISION_BY_ZERO"
+ );
+
+ require(
+ !isRoundingErrorFloor(
+ numerator,
+ denominator,
+ target
+ ),
+ "ROUNDING_ERROR"
+ );
+
+ partialAmount = safeDiv(
+ safeMul(numerator, target),
+ denominator
+ );
+ return partialAmount;
+ }
+
+ /// @dev Calculates partial value given a numerator and denominator rounded down.
+ /// Reverts if rounding error is >= 0.1%
+ /// @param numerator Numerator.
+ /// @param denominator Denominator.
+ /// @param target Value to calculate partial of.
+ /// @return Partial value of target rounded up.
+ function safeGetPartialAmountCeil(
+ uint256 numerator,
+ uint256 denominator,
+ uint256 target
+ )
+ internal
+ pure
+ returns (uint256 partialAmount)
+ {
+ require(
+ denominator > 0,
+ "DIVISION_BY_ZERO"
+ );
+
+ require(
+ !isRoundingErrorCeil(
+ numerator,
+ denominator,
+ target
+ ),
+ "ROUNDING_ERROR"
+ );
+
+ // safeDiv computes `floor(a / b)`. We use the identity (a, b integer):
+ // ceil(a / b) = floor((a + b - 1) / b)
+ // To implement `ceil(a / b)` using safeDiv.
+ partialAmount = safeDiv(
+ safeAdd(
+ safeMul(numerator, target),
+ safeSub(denominator, 1)
+ ),
+ denominator
+ );
+ return partialAmount;
+ }
+
+ /// @dev Calculates partial value given a numerator and denominator rounded down.
+ /// @param numerator Numerator.
+ /// @param denominator Denominator.
+ /// @param target Value to calculate partial of.
+ /// @return Partial value of target rounded down.
+ function getPartialAmountFloor(
+ uint256 numerator,
+ uint256 denominator,
+ uint256 target
+ )
+ internal
+ pure
+ returns (uint256 partialAmount)
+ {
+ require(
+ denominator > 0,
+ "DIVISION_BY_ZERO"
+ );
+
+ partialAmount = safeDiv(
+ safeMul(numerator, target),
+ denominator
+ );
+ return partialAmount;
+ }
+
+ /// @dev Calculates partial value given a numerator and denominator rounded down.
+ /// @param numerator Numerator.
+ /// @param denominator Denominator.
+ /// @param target Value to calculate partial of.
+ /// @return Partial value of target rounded up.
+ function getPartialAmountCeil(
+ uint256 numerator,
+ uint256 denominator,
+ uint256 target
+ )
+ internal
+ pure
+ returns (uint256 partialAmount)
+ {
+ require(
+ denominator > 0,
+ "DIVISION_BY_ZERO"
+ );
+
+ // safeDiv computes `floor(a / b)`. We use the identity (a, b integer):
+ // ceil(a / b) = floor((a + b - 1) / b)
+ // To implement `ceil(a / b)` using safeDiv.
+ partialAmount = safeDiv(
+ safeAdd(
+ safeMul(numerator, target),
+ safeSub(denominator, 1)
+ ),
+ denominator
+ );
+ return partialAmount;
+ }
+
+ /// @dev Checks if rounding error >= 0.1% when rounding down.
+ /// @param numerator Numerator.
+ /// @param denominator Denominator.
+ /// @param target Value to multiply with numerator/denominator.
+ /// @return Rounding error is present.
+ function isRoundingErrorFloor(
+ uint256 numerator,
+ uint256 denominator,
+ uint256 target
+ )
+ internal
+ pure
+ returns (bool isError)
+ {
+ require(
+ denominator > 0,
+ "DIVISION_BY_ZERO"
+ );
+
+ // The absolute rounding error is the difference between the rounded
+ // value and the ideal value. The relative rounding error is the
+ // absolute rounding error divided by the absolute value of the
+ // ideal value. This is undefined when the ideal value is zero.
+ //
+ // The ideal value is `numerator * target / denominator`.
+ // Let's call `numerator * target % denominator` the remainder.
+ // The absolute error is `remainder / denominator`.
+ //
+ // When the ideal value is zero, we require the absolute error to
+ // be zero. Fortunately, this is always the case. The ideal value is
+ // zero iff `numerator == 0` and/or `target == 0`. In this case the
+ // remainder and absolute error are also zero.
+ if (target == 0 || numerator == 0) {
+ return false;
+ }
+
+ // Otherwise, we want the relative rounding error to be strictly
+ // less than 0.1%.
+ // The relative error is `remainder / (numerator * target)`.
+ // We want the relative error less than 1 / 1000:
+ // remainder / (numerator * denominator) < 1 / 1000
+ // or equivalently:
+ // 1000 * remainder < numerator * target
+ // so we have a rounding error iff:
+ // 1000 * remainder >= numerator * target
+ uint256 remainder = mulmod(
+ target,
+ numerator,
+ denominator
+ );
+ isError = safeMul(1000, remainder) >= safeMul(numerator, target);
+ return isError;
+ }
+
+ /// @dev Checks if rounding error >= 0.1% when rounding up.
+ /// @param numerator Numerator.
+ /// @param denominator Denominator.
+ /// @param target Value to multiply with numerator/denominator.
+ /// @return Rounding error is present.
+ function isRoundingErrorCeil(
+ uint256 numerator,
+ uint256 denominator,
+ uint256 target
+ )
+ internal
+ pure
+ returns (bool isError)
+ {
+ require(
+ denominator > 0,
+ "DIVISION_BY_ZERO"
+ );
+
+ // See the comments in `isRoundingError`.
+ if (target == 0 || numerator == 0) {
+ // When either is zero, the ideal value and rounded value are zero
+ // and there is no rounding error. (Although the relative error
+ // is undefined.)
+ return false;
+ }
+ // Compute remainder as before
+ uint256 remainder = mulmod(
+ target,
+ numerator,
+ denominator
+ );
+ remainder = safeSub(denominator, remainder) % denominator;
+ isError = safeMul(1000, remainder) >= safeMul(numerator, target);
+ return isError;
+ }
+}
diff --git a/contracts/libs/contracts/libs/LibOrder.sol b/contracts/libs/contracts/libs/LibOrder.sol
new file mode 100644
index 000000000..0fe7c2161
--- /dev/null
+++ b/contracts/libs/contracts/libs/LibOrder.sol
@@ -0,0 +1,145 @@
+/*
+
+ Copyright 2018 ZeroEx Intl.
+
+ 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;
+
+import "./LibEIP712.sol";
+
+
+contract LibOrder is
+ LibEIP712
+{
+ // Hash for the EIP712 Order Schema
+ bytes32 constant internal EIP712_ORDER_SCHEMA_HASH = keccak256(abi.encodePacked(
+ "Order(",
+ "address makerAddress,",
+ "address takerAddress,",
+ "address feeRecipientAddress,",
+ "address senderAddress,",
+ "uint256 makerAssetAmount,",
+ "uint256 takerAssetAmount,",
+ "uint256 makerFee,",
+ "uint256 takerFee,",
+ "uint256 expirationTimeSeconds,",
+ "uint256 salt,",
+ "bytes makerAssetData,",
+ "bytes takerAssetData",
+ ")"
+ ));
+
+ // A valid order remains fillable until it is expired, fully filled, or cancelled.
+ // An order's state is unaffected by external factors, like account balances.
+ enum OrderStatus {
+ INVALID, // Default value
+ INVALID_MAKER_ASSET_AMOUNT, // Order does not have a valid maker asset amount
+ INVALID_TAKER_ASSET_AMOUNT, // Order does not have a valid taker asset amount
+ FILLABLE, // Order is fillable
+ EXPIRED, // Order has already expired
+ FULLY_FILLED, // Order is fully filled
+ CANCELLED // Order has been cancelled
+ }
+
+ // solhint-disable max-line-length
+ 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.
+ }
+ // solhint-enable max-line-length
+
+ struct OrderInfo {
+ uint8 orderStatus; // Status that describes order's validity and fillability.
+ bytes32 orderHash; // EIP712 hash of the order (see LibOrder.getOrderHash).
+ uint256 orderTakerAssetFilledAmount; // Amount of order that has already been filled.
+ }
+
+ /// @dev Calculates Keccak-256 hash of the order.
+ /// @param order The order structure.
+ /// @return Keccak-256 EIP712 hash of the order.
+ function getOrderHash(Order memory order)
+ internal
+ view
+ returns (bytes32 orderHash)
+ {
+ orderHash = hashEIP712Message(hashOrder(order));
+ return orderHash;
+ }
+
+ /// @dev Calculates EIP712 hash of the order.
+ /// @param order The order structure.
+ /// @return EIP712 hash of the order.
+ function hashOrder(Order memory order)
+ internal
+ pure
+ returns (bytes32 result)
+ {
+ bytes32 schemaHash = EIP712_ORDER_SCHEMA_HASH;
+ bytes32 makerAssetDataHash = keccak256(order.makerAssetData);
+ bytes32 takerAssetDataHash = keccak256(order.takerAssetData);
+
+ // Assembly for more efficiently computing:
+ // keccak256(abi.encodePacked(
+ // EIP712_ORDER_SCHEMA_HASH,
+ // bytes32(order.makerAddress),
+ // bytes32(order.takerAddress),
+ // bytes32(order.feeRecipientAddress),
+ // bytes32(order.senderAddress),
+ // order.makerAssetAmount,
+ // order.takerAssetAmount,
+ // order.makerFee,
+ // order.takerFee,
+ // order.expirationTimeSeconds,
+ // order.salt,
+ // keccak256(order.makerAssetData),
+ // keccak256(order.takerAssetData)
+ // ));
+
+ assembly {
+ // Calculate memory addresses that will be swapped out before hashing
+ let pos1 := sub(order, 32)
+ let pos2 := add(order, 320)
+ let pos3 := add(order, 352)
+
+ // Backup
+ let temp1 := mload(pos1)
+ let temp2 := mload(pos2)
+ let temp3 := mload(pos3)
+
+ // Hash in place
+ mstore(pos1, schemaHash)
+ mstore(pos2, makerAssetDataHash)
+ mstore(pos3, takerAssetDataHash)
+ result := keccak256(pos1, 416)
+
+ // Restore
+ mstore(pos1, temp1)
+ mstore(pos2, temp2)
+ mstore(pos3, temp3)
+ }
+ return result;
+ }
+}
diff --git a/contracts/libs/contracts/test/TestLibs/TestLibs.sol b/contracts/libs/contracts/test/TestLibs/TestLibs.sol
new file mode 100644
index 000000000..bd5f9f9da
--- /dev/null
+++ b/contracts/libs/contracts/test/TestLibs/TestLibs.sol
@@ -0,0 +1,152 @@
+/*
+
+ Copyright 2018 ZeroEx Intl.
+
+ 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 "../../libs/LibMath.sol";
+import "../../libs/LibOrder.sol";
+import "../../libs/LibFillResults.sol";
+import "../../libs/LibAbiEncoder.sol";
+
+
+contract TestLibs is
+ LibMath,
+ LibOrder,
+ LibFillResults,
+ LibAbiEncoder
+{
+ function publicAbiEncodeFillOrder(
+ Order memory order,
+ uint256 takerAssetFillAmount,
+ bytes memory signature
+ )
+ public
+ pure
+ returns (bytes memory fillOrderCalldata)
+ {
+ fillOrderCalldata = abiEncodeFillOrder(
+ order,
+ takerAssetFillAmount,
+ signature
+ );
+ return fillOrderCalldata;
+ }
+
+ function publicGetPartialAmountFloor(
+ uint256 numerator,
+ uint256 denominator,
+ uint256 target
+ )
+ public
+ pure
+ returns (uint256 partialAmount)
+ {
+ partialAmount = getPartialAmountFloor(
+ numerator,
+ denominator,
+ target
+ );
+ return partialAmount;
+ }
+
+ function publicGetPartialAmountCeil(
+ uint256 numerator,
+ uint256 denominator,
+ uint256 target
+ )
+ public
+ pure
+ returns (uint256 partialAmount)
+ {
+ partialAmount = getPartialAmountCeil(
+ numerator,
+ denominator,
+ target
+ );
+ return partialAmount;
+ }
+
+ function publicIsRoundingErrorFloor(
+ uint256 numerator,
+ uint256 denominator,
+ uint256 target
+ )
+ public
+ pure
+ returns (bool isError)
+ {
+ isError = isRoundingErrorFloor(
+ numerator,
+ denominator,
+ target
+ );
+ return isError;
+ }
+
+ function publicIsRoundingErrorCeil(
+ uint256 numerator,
+ uint256 denominator,
+ uint256 target
+ )
+ public
+ pure
+ returns (bool isError)
+ {
+ isError = isRoundingErrorCeil(
+ numerator,
+ denominator,
+ target
+ );
+ return isError;
+ }
+
+ function publicGetOrderHash(Order memory order)
+ public
+ view
+ returns (bytes32 orderHash)
+ {
+ orderHash = getOrderHash(order);
+ return orderHash;
+ }
+
+ function getOrderSchemaHash()
+ public
+ pure
+ returns (bytes32)
+ {
+ return EIP712_ORDER_SCHEMA_HASH;
+ }
+
+ function getDomainSeparatorSchemaHash()
+ public
+ pure
+ returns (bytes32)
+ {
+ return EIP712_DOMAIN_SEPARATOR_SCHEMA_HASH;
+ }
+
+ function publicAddFillResults(FillResults memory totalFillResults, FillResults memory singleFillResults)
+ public
+ pure
+ returns (FillResults memory)
+ {
+ addFillResults(totalFillResults, singleFillResults);
+ return totalFillResults;
+ }
+}
diff --git a/contracts/libs/package.json b/contracts/libs/package.json
new file mode 100644
index 000000000..74288be76
--- /dev/null
+++ b/contracts/libs/package.json
@@ -0,0 +1,92 @@
+{
+ "private": true,
+ "name": "@0x/contracts-libs",
+ "version": "1.0.0",
+ "engines": {
+ "node": ">=6.12"
+ },
+ "description": "Smart contract libs of 0x protocol",
+ "main": "lib/src/index.js",
+ "directories": {
+ "test": "test"
+ },
+ "scripts": {
+ "build": "yarn pre_build && tsc -b",
+ "build:ci": "yarn build",
+ "pre_build": "run-s compile generate_contract_wrappers",
+ "test": "yarn run_mocha",
+ "rebuild_and_test": "run-s build test",
+ "test:coverage": "SOLIDITY_COVERAGE=true run-s build run_mocha coverage:report:text coverage:report:lcov",
+ "test:profiler": "SOLIDITY_PROFILER=true run-s build run_mocha profiler:report:html",
+ "test:trace": "SOLIDITY_REVERT_TRACE=true run-s build run_mocha",
+ "run_mocha":
+ "mocha --require source-map-support/register --require make-promises-safe 'lib/test/**/*.js' --timeout 100000 --bail --exit",
+ "compile": "sol-compiler --contracts-dir contracts",
+ "clean": "shx rm -rf lib generated-artifacts generated-wrappers",
+ "generate_contract_wrappers": "abi-gen --abis ${npm_package_config_abis} --template ../../node_modules/@0x/abi-gen-templates/contract.handlebars --partials '../../node_modules/@0x/abi-gen-templates/partials/**/*.handlebars' --output generated-wrappers --backend ethers",
+ "lint": "tslint --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts",
+ "coverage:report:text": "istanbul report text",
+ "coverage:report:html": "istanbul report html && open coverage/index.html",
+ "profiler:report:html": "istanbul report html && open coverage/index.html",
+ "coverage:report:lcov": "istanbul report lcov",
+ "test:circleci": "yarn test",
+ "lint-contracts": "solhint contracts/**/**/**/**/*.sol"
+ },
+ "config": {
+ "abis": "generated-artifacts/@(LibMath|LibOrder|LibFillResults|LibAbiEncoder|TestLibs|LibEIP712).json"
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/0xProject/0x-monorepo.git"
+ },
+ "license": "Apache-2.0",
+ "bugs": {
+ "url": "https://github.com/0xProject/0x-monorepo/issues"
+ },
+ "homepage": "https://github.com/0xProject/0x-monorepo/contracts/libs/README.md",
+ "devDependencies": {
+ "@0x/contracts-test-utils": "^1.0.0",
+ "@0x/abi-gen": "^1.0.17",
+ "@0x/dev-utils": "^1.0.19",
+ "@0x/sol-compiler": "^1.1.14",
+ "@0x/sol-cov": "^2.1.14",
+ "@0x/subproviders": "^2.1.6",
+ "@0x/tslint-config": "^1.0.10",
+ "@types/bn.js": "^4.11.0",
+ "@types/lodash": "4.14.104",
+ "@types/node": "*",
+ "@types/yargs": "^10.0.0",
+ "chai": "^4.0.1",
+ "chai-as-promised": "^7.1.0",
+ "chai-bignumber": "^2.0.1",
+ "dirty-chai": "^2.0.1",
+ "make-promises-safe": "^1.1.0",
+ "ethereumjs-abi": "0.6.5",
+ "mocha": "^4.1.0",
+ "npm-run-all": "^4.1.2",
+ "shx": "^0.2.2",
+ "solc": "^0.4.24",
+ "solhint": "^1.2.1",
+ "tslint": "5.11.0",
+ "typescript": "3.0.1",
+ "yargs": "^10.0.3"
+ },
+ "dependencies": {
+ "@0x/base-contract": "^3.0.8",
+ "@0x/order-utils": "^3.0.4",
+ "@0x/contracts-multisig": "^1.0.0",
+ "@0x/contracts-utils": "^1.0.0",
+ "@0x/types": "^1.3.0",
+ "@0x/typescript-typings": "^3.0.4",
+ "@0x/utils": "^2.0.6",
+ "@0x/web3-wrapper": "^3.1.6",
+ "@types/js-combinatorics": "^0.5.29",
+ "bn.js": "^4.11.8",
+ "ethereum-types": "^1.1.2",
+ "ethereumjs-util": "^5.1.1",
+ "lodash": "^4.17.5"
+ },
+ "publishConfig": {
+ "access": "public"
+ }
+}
diff --git a/contracts/libs/src/artifacts/index.ts b/contracts/libs/src/artifacts/index.ts
new file mode 100644
index 000000000..3955bbe2b
--- /dev/null
+++ b/contracts/libs/src/artifacts/index.ts
@@ -0,0 +1,17 @@
+import { ContractArtifact } from 'ethereum-types';
+
+import * as LibAbiEncoder from '../../generated-artifacts/LibAbiEncoder.json';
+import * as LibEIP721 from '../../generated-artifacts/LibEIP712.json';
+import * as LibFillResults from '../../generated-artifacts/LibFillResults.json';
+import * as LibMath from '../../generated-artifacts/LibMath.json';
+import * as LibOrder from '../../generated-artifacts/LibOrder.json';
+import * as TestLibs from '../../generated-artifacts/TestLibs.json';
+
+export const artifacts = {
+ TestLibs: TestLibs as ContractArtifact,
+ LibAbiEncoder: LibAbiEncoder as ContractArtifact,
+ LibFillResults: LibFillResults as ContractArtifact,
+ LibMath: LibMath as ContractArtifact,
+ LibOrder: LibOrder as ContractArtifact,
+ LibEIP721: LibEIP721 as ContractArtifact,
+};
diff --git a/contracts/libs/src/index.ts b/contracts/libs/src/index.ts
new file mode 100644
index 000000000..d55f08ea2
--- /dev/null
+++ b/contracts/libs/src/index.ts
@@ -0,0 +1,2 @@
+export * from './artifacts';
+export * from './wrappers';
diff --git a/contracts/libs/src/wrappers/index.ts b/contracts/libs/src/wrappers/index.ts
new file mode 100644
index 000000000..baaae6e34
--- /dev/null
+++ b/contracts/libs/src/wrappers/index.ts
@@ -0,0 +1,6 @@
+export * from '../../generated-wrappers/test_libs';
+export * from '../../generated-wrappers/lib_abi_encoder';
+export * from '../../generated-wrappers/lib_fill_results';
+export * from '../../generated-wrappers/lib_math';
+export * from '../../generated-wrappers/lib_order';
+export * from '../../generated-wrappers/lib_e_i_p712';
diff --git a/contracts/libs/test/exchange/libs.ts b/contracts/libs/test/exchange/libs.ts
new file mode 100644
index 000000000..44ff6a844
--- /dev/null
+++ b/contracts/libs/test/exchange/libs.ts
@@ -0,0 +1,125 @@
+import {
+ addressUtils,
+ chaiSetup,
+ constants,
+ OrderFactory,
+ provider,
+ txDefaults,
+ web3Wrapper,
+} from '@0x/contracts-test-utils';
+import { BlockchainLifecycle } from '@0x/dev-utils';
+import { assetDataUtils, orderHashUtils } from '@0x/order-utils';
+import { SignedOrder } from '@0x/types';
+import { BigNumber } from '@0x/utils';
+import * as chai from 'chai';
+
+import { TestLibsContract } from '../../generated-wrappers/test_libs';
+import { artifacts } from '../../src/artifacts';
+
+chaiSetup.configure();
+const expect = chai.expect;
+
+const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
+
+describe('Exchange libs', () => {
+ let signedOrder: SignedOrder;
+ let orderFactory: OrderFactory;
+ let libs: TestLibsContract;
+
+ before(async () => {
+ await blockchainLifecycle.startAsync();
+ });
+ after(async () => {
+ await blockchainLifecycle.revertAsync();
+ });
+ before(async () => {
+ const accounts = await web3Wrapper.getAvailableAddressesAsync();
+ const makerAddress = accounts[0];
+ libs = await TestLibsContract.deployFrom0xArtifactAsync(artifacts.TestLibs, provider, txDefaults);
+
+ const defaultOrderParams = {
+ ...constants.STATIC_ORDER_PARAMS,
+ exchangeAddress: libs.address,
+ makerAddress,
+ feeRecipientAddress: addressUtils.generatePseudoRandomAddress(),
+ makerAssetData: assetDataUtils.encodeERC20AssetData(addressUtils.generatePseudoRandomAddress()),
+ takerAssetData: assetDataUtils.encodeERC20AssetData(addressUtils.generatePseudoRandomAddress()),
+ };
+ const privateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddress)];
+ orderFactory = new OrderFactory(privateKey, defaultOrderParams);
+ });
+
+ beforeEach(async () => {
+ await blockchainLifecycle.startAsync();
+ });
+ afterEach(async () => {
+ await blockchainLifecycle.revertAsync();
+ });
+ // Note(albrow): These tests are designed to be supplemental to the
+ // combinatorial tests in test/exchange/internal. They test specific edge
+ // cases that are not covered by the combinatorial tests.
+ describe('LibMath', () => {
+ describe('isRoundingError', () => {
+ it('should return true if there is a rounding error of 0.1%', async () => {
+ const numerator = new BigNumber(20);
+ const denominator = new BigNumber(999);
+ const target = new BigNumber(50);
+ // rounding error = ((20*50/999) - floor(20*50/999)) / (20*50/999) = 0.1%
+ const isRoundingError = await libs.publicIsRoundingErrorFloor.callAsync(numerator, denominator, target);
+ expect(isRoundingError).to.be.true();
+ });
+ it('should return false if there is a rounding of 0.09%', async () => {
+ const numerator = new BigNumber(20);
+ const denominator = new BigNumber(9991);
+ const target = new BigNumber(500);
+ // rounding error = ((20*500/9991) - floor(20*500/9991)) / (20*500/9991) = 0.09%
+ const isRoundingError = await libs.publicIsRoundingErrorFloor.callAsync(numerator, denominator, target);
+ expect(isRoundingError).to.be.false();
+ });
+ it('should return true if there is a rounding error of 0.11%', async () => {
+ const numerator = new BigNumber(20);
+ const denominator = new BigNumber(9989);
+ const target = new BigNumber(500);
+ // rounding error = ((20*500/9989) - floor(20*500/9989)) / (20*500/9989) = 0.011%
+ const isRoundingError = await libs.publicIsRoundingErrorFloor.callAsync(numerator, denominator, target);
+ expect(isRoundingError).to.be.true();
+ });
+ });
+ describe('isRoundingErrorCeil', () => {
+ it('should return true if there is a rounding error of 0.1%', async () => {
+ const numerator = new BigNumber(20);
+ const denominator = new BigNumber(1001);
+ const target = new BigNumber(50);
+ // rounding error = (ceil(20*50/1001) - (20*50/1001)) / (20*50/1001) = 0.1%
+ const isRoundingError = await libs.publicIsRoundingErrorCeil.callAsync(numerator, denominator, target);
+ expect(isRoundingError).to.be.true();
+ });
+ it('should return false if there is a rounding of 0.09%', async () => {
+ const numerator = new BigNumber(20);
+ const denominator = new BigNumber(10009);
+ const target = new BigNumber(500);
+ // rounding error = (ceil(20*500/10009) - (20*500/10009)) / (20*500/10009) = 0.09%
+ const isRoundingError = await libs.publicIsRoundingErrorCeil.callAsync(numerator, denominator, target);
+ expect(isRoundingError).to.be.false();
+ });
+ it('should return true if there is a rounding error of 0.11%', async () => {
+ const numerator = new BigNumber(20);
+ const denominator = new BigNumber(10011);
+ const target = new BigNumber(500);
+ // rounding error = (ceil(20*500/10011) - (20*500/10011)) / (20*500/10011) = 0.11%
+ const isRoundingError = await libs.publicIsRoundingErrorCeil.callAsync(numerator, denominator, target);
+ expect(isRoundingError).to.be.true();
+ });
+ });
+ });
+
+ describe('LibOrder', () => {
+ describe('getOrderHash', () => {
+ it('should output the correct orderHash', async () => {
+ signedOrder = await orderFactory.newSignedOrderAsync();
+ const orderHashHex = await libs.publicGetOrderHash.callAsync(signedOrder);
+ expect(orderHashUtils.getOrderHashHex(signedOrder)).to.be.equal(orderHashHex);
+ });
+ });
+ });
+});
diff --git a/contracts/libs/test/global_hooks.ts b/contracts/libs/test/global_hooks.ts
new file mode 100644
index 000000000..f8ace376a
--- /dev/null
+++ b/contracts/libs/test/global_hooks.ts
@@ -0,0 +1,17 @@
+import { env, EnvVars } from '@0x/dev-utils';
+
+import { coverage, profiler, provider } from '@0x/contracts-test-utils';
+before('start web3 provider', () => {
+ provider.start();
+});
+after('generate coverage report', async () => {
+ if (env.parseBoolean(EnvVars.SolidityCoverage)) {
+ const coverageSubprovider = coverage.getCoverageSubproviderSingleton();
+ await coverageSubprovider.writeCoverageAsync();
+ }
+ if (env.parseBoolean(EnvVars.SolidityProfiler)) {
+ const profilerSubprovider = profiler.getProfilerSubproviderSingleton();
+ await profilerSubprovider.writeProfilerOutputAsync();
+ }
+ provider.stop();
+});
diff --git a/contracts/libs/tsconfig.json b/contracts/libs/tsconfig.json
new file mode 100644
index 000000000..27ca35085
--- /dev/null
+++ b/contracts/libs/tsconfig.json
@@ -0,0 +1,18 @@
+{
+ "extends": "../../tsconfig",
+ "compilerOptions": {
+ "outDir": "lib",
+ "rootDir": ".",
+ "resolveJsonModule": true
+ },
+ "include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*"],
+ "files": [
+ "./generated-artifacts/TestLibs.json",
+ "./generated-artifacts/LibOrder.json",
+ "./generated-artifacts/LibFillResults.json",
+ "./generated-artifacts/LibAbiEncoder.json",
+ "./generated-artifacts/LibEIP712.json",
+ "./generated-artifacts/LibMath.json"
+ ],
+ "exclude": ["./deploy/solc/solc_bin"]
+}
diff --git a/contracts/libs/tslint.json b/contracts/libs/tslint.json
new file mode 100644
index 000000000..1bb3ac2a2
--- /dev/null
+++ b/contracts/libs/tslint.json
@@ -0,0 +1,6 @@
+{
+ "extends": ["@0x/tslint-config"],
+ "rules": {
+ "custom-no-magic-numbers": false
+ }
+}