diff options
author | Leonid Logvinov <logvinov.leon@gmail.com> | 2018-12-11 06:37:48 +0800 |
---|---|---|
committer | Leonid Logvinov <logvinov.leon@gmail.com> | 2018-12-11 06:44:49 +0800 |
commit | 9f5eeed30930eea59e9922569a15cb5d689f3eeb (patch) | |
tree | 74dac8603dae440bcaa28245fd5af49815284840 /contracts/core | |
parent | 14ea4ee1d3fd1a34c1747b928d9b463787e603e7 (diff) | |
download | dexon-sol-tools-9f5eeed30930eea59e9922569a15cb5d689f3eeb.tar dexon-sol-tools-9f5eeed30930eea59e9922569a15cb5d689f3eeb.tar.gz dexon-sol-tools-9f5eeed30930eea59e9922569a15cb5d689f3eeb.tar.bz2 dexon-sol-tools-9f5eeed30930eea59e9922569a15cb5d689f3eeb.tar.lz dexon-sol-tools-9f5eeed30930eea59e9922569a15cb5d689f3eeb.tar.xz dexon-sol-tools-9f5eeed30930eea59e9922569a15cb5d689f3eeb.tar.zst dexon-sol-tools-9f5eeed30930eea59e9922569a15cb5d689f3eeb.zip |
Rename core package to protocol
Diffstat (limited to 'contracts/core')
57 files changed, 0 insertions, 14971 deletions
diff --git a/contracts/core/.solhint.json b/contracts/core/.solhint.json deleted file mode 100644 index 076afe9f3..000000000 --- a/contracts/core/.solhint.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "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/core/CHANGELOG.json b/contracts/core/CHANGELOG.json deleted file mode 100644 index 371f18cd4..000000000 --- a/contracts/core/CHANGELOG.json +++ /dev/null @@ -1,121 +0,0 @@ -[ - { - "name": "MultiAssetProxy", - "version": "1.0.0", - "changes": [ - { - "note": "Add MultiAssetProxy implementation", - "pr": 1224 - } - ] - }, - { - "name": "OrderValidator", - "version": "1.0.1", - "changes": [ - { - "note": "remove `getApproved` check from ERC721 approval query", - "pr": 1149 - } - ] - }, - { - "name": "Forwarder", - "version": "1.1.0", - "changes": [ - { - "note": "Round up when calculating remaining amounts in marketBuy functions", - "pr": 1162, - "networks": { - "1": "0x5468a1dc173652ee28d249c271fa9933144746b1", - "3": "0x2240dab907db71e64d3e0dba4800c83b5c502d4e", - "42": "0x17992e4ffb22730138e4b62aaa6367fa9d3699a6" - } - } - ] - }, - { - "name": "Forwarder", - "version": "1.0.0", - "changes": [ - { - "note": "protocol v2 deploy", - "networks": { - "1": "0x7afc2d5107af94c462a194d2c21b5bdd238709d6", - "3": "0x3983e204b12b3c02fb0638caf2cd406a62e0ead3", - "42": "0xd85e2fa7e7e252b27b01bf0d65c946959d2f45b8" - } - } - ] - }, - { - "name": "OrderValidator", - "version": "1.0.0", - "changes": [ - { - "note": "protocol v2 deploy", - "networks": { - "1": "0x9463e518dea6810309563c81d5266c1b1d149138", - "3": "0x90431a90516ab49af23a0530e04e8c7836e7122f", - "42": "0xb389da3d204b412df2f75c6afb3d0a7ce0bc283d" - } - } - ] - }, - { - "name": "Exchange", - "version": "2.0.0", - "changes": [ - { - "note": "protocol v2 deploy", - "networks": { - "1": "0x4f833a24e1f95d70f028921e27040ca56e09ab0b", - "3": "0x4530c0483a1633c7a1c97d2c53721caff2caaaaf", - "42": "0x35dd2932454449b14cee11a94d3674a936d5d7b2" - } - } - ] - }, - { - "name": "ERC20Proxy", - "version": "1.0.0", - "changes": [ - { - "note": "protocol v2 deploy", - "networks": { - "1": "0x2240dab907db71e64d3e0dba4800c83b5c502d4e", - "3": "0xb1408f4c245a23c31b98d2c626777d4c0d766caa", - "42": "0xf1ec01d6236d3cd881a0bf0130ea25fe4234003e" - } - } - ] - }, - { - "name": "ERC721Proxy", - "version": "1.0.0", - "changes": [ - { - "note": "protocol v2 deploy", - "networks": { - "1": "0x208e41fb445f1bb1b6780d58356e81405f3e6127", - "3": "0xe654aac058bfbf9f83fcaee7793311dd82f6ddb4", - "42": "0x2a9127c745688a165106c11cd4d647d2220af821" - } - } - ] - }, - { - "name": "AssetProxyOwner", - "version": "1.0.0", - "changes": [ - { - "note": "protocol v2 deploy", - "networks": { - "1": "0x17992e4ffb22730138e4b62aaa6367fa9d3699a6", - "3": "0xf5fa5b5fed2727a0e44ac67f6772e97977aa358b", - "42": "0x2c824d2882baa668e0d5202b1e7f2922278703f8" - } - } - ] - } -] diff --git a/contracts/core/README.md b/contracts/core/README.md deleted file mode 100644 index d055705c2..000000000 --- a/contracts/core/README.md +++ /dev/null @@ -1,74 +0,0 @@ -## Contracts - -Smart contracts that implement the 0x protocol. Addresses of the deployed contracts can be found in the 0x [wiki](https://0xproject.com/wiki#Deployed-Addresses) or the [CHANGELOG](./CHANGELOG.json) of this package. - -## Usage - -Contracts that make up and interact with version 2.0.0 of the protocol can be found in the [contracts](./contracts) directory. The contents of this directory are broken down into the following subdirectories: - -* [protocol](./contracts/protocol) - * This directory contains the contracts that make up version 2.0.0. A full specification can be found [here](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md). -* [test](./contracts/test) - * This directory contains mocks and other contracts that are used solely for testing contracts within the other directories. - -## Bug bounty - -A bug bounty for the 2.0.0 contracts is ongoing! Instructions can be found [here](https://0xproject.com/wiki#Bug-Bounty). - -## 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-core yarn build -``` - -Or continuously rebuild on change: - -```bash -PKG=@0x/contracts-core 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/core/compiler.json b/contracts/core/compiler.json deleted file mode 100644 index 10e5bb0a1..000000000 --- a/contracts/core/compiler.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "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": [ - "AssetProxyOwner", - "ERC20Proxy", - "ERC721Proxy", - "Exchange", - "MixinAuthorizable", - "MultiAssetProxy", - "TestAssetProxyOwner", - "TestAssetProxyDispatcher", - "TestExchangeInternals", - "TestSignatureValidator", - "TestStaticCallReceiver" - ] -} diff --git a/contracts/core/contracts/protocol/AssetProxy/ERC20Proxy.sol b/contracts/core/contracts/protocol/AssetProxy/ERC20Proxy.sol deleted file mode 100644 index 258443bca..000000000 --- a/contracts/core/contracts/protocol/AssetProxy/ERC20Proxy.sol +++ /dev/null @@ -1,184 +0,0 @@ -/* - - 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 "./MixinAuthorizable.sol"; - - -contract ERC20Proxy is - MixinAuthorizable -{ - // Id of this proxy. - bytes4 constant internal PROXY_ID = bytes4(keccak256("ERC20Token(address)")); - - // solhint-disable-next-line payable-fallback - function () - external - { - assembly { - // The first 4 bytes of calldata holds the function selector - let selector := and(calldataload(0), 0xffffffff00000000000000000000000000000000000000000000000000000000) - - // `transferFrom` will be called with the following parameters: - // assetData Encoded byte array. - // from Address to transfer asset from. - // to Address to transfer asset to. - // amount Amount of asset to transfer. - // bytes4(keccak256("transferFrom(bytes,address,address,uint256)")) = 0xa85e59e4 - if eq(selector, 0xa85e59e400000000000000000000000000000000000000000000000000000000) { - - // To lookup a value in a mapping, we load from the storage location keccak256(k, p), - // where k is the key left padded to 32 bytes and p is the storage slot - let start := mload(64) - mstore(start, and(caller, 0xffffffffffffffffffffffffffffffffffffffff)) - mstore(add(start, 32), authorized_slot) - - // Revert if authorized[msg.sender] == false - if iszero(sload(keccak256(start, 64))) { - // Revert with `Error("SENDER_NOT_AUTHORIZED")` - mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000) - mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000) - mstore(64, 0x0000001553454e4445525f4e4f545f415554484f52495a454400000000000000) - mstore(96, 0) - revert(0, 100) - } - - // `transferFrom`. - // The function is marked `external`, so no abi decodeding is done for - // us. Instead, we expect the `calldata` memory to contain the - // following: - // - // | Area | Offset | Length | Contents | - // |----------|--------|---------|-------------------------------------| - // | Header | 0 | 4 | function selector | - // | Params | | 4 * 32 | function parameters: | - // | | 4 | | 1. offset to assetData (*) | - // | | 36 | | 2. from | - // | | 68 | | 3. to | - // | | 100 | | 4. amount | - // | Data | | | assetData: | - // | | 132 | 32 | assetData Length | - // | | 164 | ** | assetData Contents | - // - // (*): offset is computed from start of function parameters, so offset - // by an additional 4 bytes in the calldata. - // - // (**): see table below to compute length of assetData Contents - // - // WARNING: The ABIv2 specification allows additional padding between - // the Params and Data section. This will result in a larger - // offset to assetData. - - // Asset data itself is encoded as follows: - // - // | Area | Offset | Length | Contents | - // |----------|--------|---------|-------------------------------------| - // | Header | 0 | 4 | function selector | - // | Params | | 1 * 32 | function parameters: | - // | | 4 | 12 + 20 | 1. token address | - - // We construct calldata for the `token.transferFrom` ABI. - // The layout of this calldata is in the table below. - // - // | Area | Offset | Length | Contents | - // |----------|--------|---------|-------------------------------------| - // | Header | 0 | 4 | function selector | - // | Params | | 3 * 32 | function parameters: | - // | | 4 | | 1. from | - // | | 36 | | 2. to | - // | | 68 | | 3. amount | - - /////// Read token address from calldata /////// - // * The token address is stored in `assetData`. - // - // * The "offset to assetData" is stored at offset 4 in the calldata (table 1). - // [assetDataOffsetFromParams = calldataload(4)] - // - // * Notes that the "offset to assetData" is relative to the "Params" area of calldata; - // add 4 bytes to account for the length of the "Header" area (table 1). - // [assetDataOffsetFromHeader = assetDataOffsetFromParams + 4] - // - // * The "token address" is offset 32+4=36 bytes into "assetData" (tables 1 & 2). - // [tokenOffset = assetDataOffsetFromHeader + 36 = calldataload(4) + 4 + 36] - let token := calldataload(add(calldataload(4), 40)) - - /////// Setup Header Area /////// - // This area holds the 4-byte `transferFrom` selector. - // Any trailing data in transferFromSelector will be - // overwritten in the next `mstore` call. - mstore(0, 0x23b872dd00000000000000000000000000000000000000000000000000000000) - - /////// Setup Params Area /////// - // We copy the fields `from`, `to` and `amount` in bulk - // from our own calldata to the new calldata. - calldatacopy(4, 36, 96) - - /////// Call `token.transferFrom` using the calldata /////// - let success := call( - gas, // forward all gas - token, // call address of token contract - 0, // don't send any ETH - 0, // pointer to start of input - 100, // length of input - 0, // write output over input - 32 // output size should be 32 bytes - ) - - /////// Check return data. /////// - // If there is no return data, we assume the token incorrectly - // does not return a bool. In this case we expect it to revert - // on failure, which was handled above. - // If the token does return data, we require that it is a single - // nonzero 32 bytes value. - // So the transfer succeeded if the call succeeded and either - // returned nothing, or returned a non-zero 32 byte value. - success := and(success, or( - iszero(returndatasize), - and( - eq(returndatasize, 32), - gt(mload(0), 0) - ) - )) - if success { - return(0, 0) - } - - // Revert with `Error("TRANSFER_FAILED")` - mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000) - mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000) - mstore(64, 0x0000000f5452414e534645525f4641494c454400000000000000000000000000) - mstore(96, 0) - revert(0, 100) - } - - // Revert if undefined function is called - revert(0, 0) - } - } - - /// @dev Gets the proxy id associated with the proxy address. - /// @return Proxy id. - function getProxyId() - external - pure - returns (bytes4) - { - return PROXY_ID; - } -} diff --git a/contracts/core/contracts/protocol/AssetProxy/ERC721Proxy.sol b/contracts/core/contracts/protocol/AssetProxy/ERC721Proxy.sol deleted file mode 100644 index 65b664b8b..000000000 --- a/contracts/core/contracts/protocol/AssetProxy/ERC721Proxy.sol +++ /dev/null @@ -1,171 +0,0 @@ -/* - - 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 "./MixinAuthorizable.sol"; - - -contract ERC721Proxy is - MixinAuthorizable -{ - // Id of this proxy. - bytes4 constant internal PROXY_ID = bytes4(keccak256("ERC721Token(address,uint256)")); - - // solhint-disable-next-line payable-fallback - function () - external - { - assembly { - // The first 4 bytes of calldata holds the function selector - let selector := and(calldataload(0), 0xffffffff00000000000000000000000000000000000000000000000000000000) - - // `transferFrom` will be called with the following parameters: - // assetData Encoded byte array. - // from Address to transfer asset from. - // to Address to transfer asset to. - // amount Amount of asset to transfer. - // bytes4(keccak256("transferFrom(bytes,address,address,uint256)")) = 0xa85e59e4 - if eq(selector, 0xa85e59e400000000000000000000000000000000000000000000000000000000) { - - // To lookup a value in a mapping, we load from the storage location keccak256(k, p), - // where k is the key left padded to 32 bytes and p is the storage slot - let start := mload(64) - mstore(start, and(caller, 0xffffffffffffffffffffffffffffffffffffffff)) - mstore(add(start, 32), authorized_slot) - - // Revert if authorized[msg.sender] == false - if iszero(sload(keccak256(start, 64))) { - // Revert with `Error("SENDER_NOT_AUTHORIZED")` - mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000) - mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000) - mstore(64, 0x0000001553454e4445525f4e4f545f415554484f52495a454400000000000000) - mstore(96, 0) - revert(0, 100) - } - - // `transferFrom`. - // The function is marked `external`, so no abi decodeding is done for - // us. Instead, we expect the `calldata` memory to contain the - // following: - // - // | Area | Offset | Length | Contents | - // |----------|--------|---------|-------------------------------------| - // | Header | 0 | 4 | function selector | - // | Params | | 4 * 32 | function parameters: | - // | | 4 | | 1. offset to assetData (*) | - // | | 36 | | 2. from | - // | | 68 | | 3. to | - // | | 100 | | 4. amount | - // | Data | | | assetData: | - // | | 132 | 32 | assetData Length | - // | | 164 | ** | assetData Contents | - // - // (*): offset is computed from start of function parameters, so offset - // by an additional 4 bytes in the calldata. - // - // (**): see table below to compute length of assetData Contents - // - // WARNING: The ABIv2 specification allows additional padding between - // the Params and Data section. This will result in a larger - // offset to assetData. - - // Asset data itself is encoded as follows: - // - // | Area | Offset | Length | Contents | - // |----------|--------|---------|-------------------------------------| - // | Header | 0 | 4 | function selector | - // | Params | | 2 * 32 | function parameters: | - // | | 4 | 12 + 20 | 1. token address | - // | | 36 | | 2. tokenId | - - // We construct calldata for the `token.transferFrom` ABI. - // The layout of this calldata is in the table below. - // - // | Area | Offset | Length | Contents | - // |----------|--------|---------|-------------------------------------| - // | Header | 0 | 4 | function selector | - // | Params | | 3 * 32 | function parameters: | - // | | 4 | | 1. from | - // | | 36 | | 2. to | - // | | 68 | | 3. tokenId | - - // There exists only 1 of each token. - // require(amount == 1, "INVALID_AMOUNT") - if sub(calldataload(100), 1) { - // Revert with `Error("INVALID_AMOUNT")` - mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000) - mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000) - mstore(64, 0x0000000e494e56414c49445f414d4f554e540000000000000000000000000000) - mstore(96, 0) - revert(0, 100) - } - - /////// Setup Header Area /////// - // This area holds the 4-byte `transferFrom` selector. - // Any trailing data in transferFromSelector will be - // overwritten in the next `mstore` call. - mstore(0, 0x23b872dd00000000000000000000000000000000000000000000000000000000) - - /////// Setup Params Area /////// - // We copy the fields `from` and `to` in bulk - // from our own calldata to the new calldata. - calldatacopy(4, 36, 64) - - // Copy `tokenId` field from our own calldata to the new calldata. - let assetDataOffset := calldataload(4) - calldatacopy(68, add(assetDataOffset, 72), 32) - - /////// Call `token.transferFrom` using the calldata /////// - let token := calldataload(add(assetDataOffset, 40)) - let success := call( - gas, // forward all gas - token, // call address of token contract - 0, // don't send any ETH - 0, // pointer to start of input - 100, // length of input - 0, // write output to null - 0 // output size is 0 bytes - ) - if success { - return(0, 0) - } - - // Revert with `Error("TRANSFER_FAILED")` - mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000) - mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000) - mstore(64, 0x0000000f5452414e534645525f4641494c454400000000000000000000000000) - mstore(96, 0) - revert(0, 100) - } - - // Revert if undefined function is called - revert(0, 0) - } - } - - /// @dev Gets the proxy id associated with the proxy address. - /// @return Proxy id. - function getProxyId() - external - pure - returns (bytes4) - { - return PROXY_ID; - } -} diff --git a/contracts/core/contracts/protocol/AssetProxy/MixinAuthorizable.sol b/contracts/core/contracts/protocol/AssetProxy/MixinAuthorizable.sol deleted file mode 100644 index 08f9b94dc..000000000 --- a/contracts/core/contracts/protocol/AssetProxy/MixinAuthorizable.sol +++ /dev/null @@ -1,117 +0,0 @@ -/* - - 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/Ownable/Ownable.sol"; -import "./mixins/MAuthorizable.sol"; - - -contract MixinAuthorizable is - Ownable, - MAuthorizable -{ - /// @dev Only authorized addresses can invoke functions with this modifier. - modifier onlyAuthorized { - require( - authorized[msg.sender], - "SENDER_NOT_AUTHORIZED" - ); - _; - } - - mapping (address => bool) public authorized; - address[] public authorities; - - /// @dev Authorizes an address. - /// @param target Address to authorize. - function addAuthorizedAddress(address target) - external - onlyOwner - { - require( - !authorized[target], - "TARGET_ALREADY_AUTHORIZED" - ); - - authorized[target] = true; - authorities.push(target); - emit AuthorizedAddressAdded(target, msg.sender); - } - - /// @dev Removes authorizion of an address. - /// @param target Address to remove authorization from. - function removeAuthorizedAddress(address target) - external - onlyOwner - { - require( - authorized[target], - "TARGET_NOT_AUTHORIZED" - ); - - delete authorized[target]; - for (uint256 i = 0; i < authorities.length; i++) { - if (authorities[i] == target) { - authorities[i] = authorities[authorities.length - 1]; - authorities.length -= 1; - break; - } - } - emit AuthorizedAddressRemoved(target, msg.sender); - } - - /// @dev Removes authorizion of an address. - /// @param target Address to remove authorization from. - /// @param index Index of target in authorities array. - function removeAuthorizedAddressAtIndex( - address target, - uint256 index - ) - external - onlyOwner - { - require( - authorized[target], - "TARGET_NOT_AUTHORIZED" - ); - require( - index < authorities.length, - "INDEX_OUT_OF_BOUNDS" - ); - require( - authorities[index] == target, - "AUTHORIZED_ADDRESS_MISMATCH" - ); - - delete authorized[target]; - authorities[index] = authorities[authorities.length - 1]; - authorities.length -= 1; - emit AuthorizedAddressRemoved(target, msg.sender); - } - - /// @dev Gets all authorized addresses. - /// @return Array of authorized addresses. - function getAuthorizedAddresses() - external - view - returns (address[] memory) - { - return authorities; - } -} diff --git a/contracts/core/contracts/protocol/AssetProxy/MultiAssetProxy.sol b/contracts/core/contracts/protocol/AssetProxy/MultiAssetProxy.sol deleted file mode 100644 index 42231e73b..000000000 --- a/contracts/core/contracts/protocol/AssetProxy/MultiAssetProxy.sol +++ /dev/null @@ -1,300 +0,0 @@ -/* - - 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 "../Exchange/MixinAssetProxyDispatcher.sol"; -import "./MixinAuthorizable.sol"; - - -contract MultiAssetProxy is - MixinAssetProxyDispatcher, - MixinAuthorizable -{ - // Id of this proxy. - bytes4 constant internal PROXY_ID = bytes4(keccak256("MultiAsset(uint256[],bytes[])")); - - // solhint-disable-next-line payable-fallback - function () - external - { - assembly { - // The first 4 bytes of calldata holds the function selector - let selector := and(calldataload(0), 0xffffffff00000000000000000000000000000000000000000000000000000000) - - // `transferFrom` will be called with the following parameters: - // assetData Encoded byte array. - // from Address to transfer asset from. - // to Address to transfer asset to. - // amount Amount of asset to transfer. - // bytes4(keccak256("transferFrom(bytes,address,address,uint256)")) = 0xa85e59e4 - if eq(selector, 0xa85e59e400000000000000000000000000000000000000000000000000000000) { - - // To lookup a value in a mapping, we load from the storage location keccak256(k, p), - // where k is the key left padded to 32 bytes and p is the storage slot - mstore(0, caller) - mstore(32, authorized_slot) - - // Revert if authorized[msg.sender] == false - if iszero(sload(keccak256(0, 64))) { - // Revert with `Error("SENDER_NOT_AUTHORIZED")` - mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000) - mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000) - mstore(64, 0x0000001553454e4445525f4e4f545f415554484f52495a454400000000000000) - mstore(96, 0) - revert(0, 100) - } - - // `transferFrom`. - // The function is marked `external`, so no abi decoding is done for - // us. Instead, we expect the `calldata` memory to contain the - // following: - // - // | Area | Offset | Length | Contents | - // |----------|--------|---------|-------------------------------------| - // | Header | 0 | 4 | function selector | - // | Params | | 4 * 32 | function parameters: | - // | | 4 | | 1. offset to assetData (*) | - // | | 36 | | 2. from | - // | | 68 | | 3. to | - // | | 100 | | 4. amount | - // | Data | | | assetData: | - // | | 132 | 32 | assetData Length | - // | | 164 | ** | assetData Contents | - // - // (*): offset is computed from start of function parameters, so offset - // by an additional 4 bytes in the calldata. - // - // (**): see table below to compute length of assetData Contents - // - // WARNING: The ABIv2 specification allows additional padding between - // the Params and Data section. This will result in a larger - // offset to assetData. - - // Load offset to `assetData` - let assetDataOffset := calldataload(4) - - // Asset data itself is encoded as follows: - // - // | Area | Offset | Length | Contents | - // |----------|-------------|---------|-------------------------------------| - // | Header | 0 | 4 | assetProxyId | - // | Params | | 2 * 32 | function parameters: | - // | | 4 | | 1. offset to amounts (*) | - // | | 36 | | 2. offset to nestedAssetData (*) | - // | Data | | | amounts: | - // | | 68 | 32 | amounts Length | - // | | 100 | a | amounts Contents | - // | | | | nestedAssetData: | - // | | 100 + a | 32 | nestedAssetData Length | - // | | 132 + a | b | nestedAssetData Contents (offsets) | - // | | 132 + a + b | | nestedAssetData[0, ..., len] | - - // In order to find the offset to `amounts`, we must add: - // 4 (function selector) - // + assetDataOffset - // + 32 (assetData len) - // + 4 (assetProxyId) - let amountsOffset := calldataload(add(assetDataOffset, 40)) - - // In order to find the offset to `nestedAssetData`, we must add: - // 4 (function selector) - // + assetDataOffset - // + 32 (assetData len) - // + 4 (assetProxyId) - // + 32 (amounts offset) - let nestedAssetDataOffset := calldataload(add(assetDataOffset, 72)) - - // In order to find the start of the `amounts` contents, we must add: - // 4 (function selector) - // + assetDataOffset - // + 32 (assetData len) - // + 4 (assetProxyId) - // + amountsOffset - // + 32 (amounts len) - let amountsContentsStart := add(assetDataOffset, add(amountsOffset, 72)) - - // Load number of elements in `amounts` - let amountsLen := calldataload(sub(amountsContentsStart, 32)) - - // In order to find the start of the `nestedAssetData` contents, we must add: - // 4 (function selector) - // + assetDataOffset - // + 32 (assetData len) - // + 4 (assetProxyId) - // + nestedAssetDataOffset - // + 32 (nestedAssetData len) - let nestedAssetDataContentsStart := add(assetDataOffset, add(nestedAssetDataOffset, 72)) - - // Load number of elements in `nestedAssetData` - let nestedAssetDataLen := calldataload(sub(nestedAssetDataContentsStart, 32)) - - // Revert if number of elements in `amounts` differs from number of elements in `nestedAssetData` - if iszero(eq(amountsLen, nestedAssetDataLen)) { - // Revert with `Error("LENGTH_MISMATCH")` - mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000) - mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000) - mstore(64, 0x0000000f4c454e4754485f4d49534d4154434800000000000000000000000000) - mstore(96, 0) - revert(0, 100) - } - - // Copy `transferFrom` selector, offset to `assetData`, `from`, and `to` from calldata to memory - calldatacopy( - 0, // memory can safely be overwritten from beginning - 0, // start of calldata - 100 // length of selector (4) and 3 params (32 * 3) - ) - - // Overwrite existing offset to `assetData` with our own - mstore(4, 128) - - // Load `amount` - let amount := calldataload(100) - - // Calculate number of bytes in `amounts` contents - let amountsByteLen := mul(amountsLen, 32) - - // Initialize `assetProxyId` and `assetProxy` to 0 - let assetProxyId := 0 - let assetProxy := 0 - - // Loop through `amounts` and `nestedAssetData`, calling `transferFrom` for each respective element - for {let i := 0} lt(i, amountsByteLen) {i := add(i, 32)} { - - // Calculate the total amount - let amountsElement := calldataload(add(amountsContentsStart, i)) - let totalAmount := mul(amountsElement, amount) - - // Revert if multiplication resulted in an overflow - if iszero(eq(div(totalAmount, amount), amountsElement)) { - // Revert with `Error("UINT256_OVERFLOW")` - mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000) - mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000) - mstore(64, 0x0000001055494e543235365f4f564552464c4f57000000000000000000000000) - mstore(96, 0) - revert(0, 100) - } - - // Write `totalAmount` to memory - mstore(100, totalAmount) - - // Load offset to `nestedAssetData[i]` - let nestedAssetDataElementOffset := calldataload(add(nestedAssetDataContentsStart, i)) - - // In order to find the start of the `nestedAssetData[i]` contents, we must add: - // 4 (function selector) - // + assetDataOffset - // + 32 (assetData len) - // + 4 (assetProxyId) - // + nestedAssetDataOffset - // + 32 (nestedAssetData len) - // + nestedAssetDataElementOffset - // + 32 (nestedAssetDataElement len) - let nestedAssetDataElementContentsStart := add(assetDataOffset, add(nestedAssetDataOffset, add(nestedAssetDataElementOffset, 104))) - - // Load length of `nestedAssetData[i]` - let nestedAssetDataElementLenStart := sub(nestedAssetDataElementContentsStart, 32) - let nestedAssetDataElementLen := calldataload(nestedAssetDataElementLenStart) - - // Revert if the `nestedAssetData` does not contain a 4 byte `assetProxyId` - if lt(nestedAssetDataElementLen, 4) { - // Revert with `Error("LENGTH_GREATER_THAN_3_REQUIRED")` - mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000) - mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000) - mstore(64, 0x0000001e4c454e4754485f475245415445525f5448414e5f335f524551554952) - mstore(96, 0x4544000000000000000000000000000000000000000000000000000000000000) - revert(0, 100) - } - - // Load AssetProxy id - let currentAssetProxyId := and( - calldataload(nestedAssetDataElementContentsStart), - 0xffffffff00000000000000000000000000000000000000000000000000000000 - ) - - // Only load `assetProxy` if `currentAssetProxyId` does not equal `assetProxyId` - // We do not need to check if `currentAssetProxyId` is 0 since `assetProxy` is also initialized to 0 - if iszero(eq(currentAssetProxyId, assetProxyId)) { - // Update `assetProxyId` - assetProxyId := currentAssetProxyId - // To lookup a value in a mapping, we load from the storage location keccak256(k, p), - // where k is the key left padded to 32 bytes and p is the storage slot - mstore(132, assetProxyId) - mstore(164, assetProxies_slot) - assetProxy := sload(keccak256(132, 64)) - } - - // Revert if AssetProxy with given id does not exist - if iszero(assetProxy) { - // Revert with `Error("ASSET_PROXY_DOES_NOT_EXIST")` - mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000) - mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000) - mstore(64, 0x0000001a41535345545f50524f58595f444f45535f4e4f545f45584953540000) - mstore(96, 0) - revert(0, 100) - } - - // Copy `nestedAssetData[i]` from calldata to memory - calldatacopy( - 132, // memory slot after `amounts[i]` - nestedAssetDataElementLenStart, // location of `nestedAssetData[i]` in calldata - add(nestedAssetDataElementLen, 32) // `nestedAssetData[i].length` plus 32 byte length - ) - - // call `assetProxy.transferFrom` - let success := call( - gas, // forward all gas - assetProxy, // call address of asset proxy - 0, // don't send any ETH - 0, // pointer to start of input - add(164, nestedAssetDataElementLen), // length of input - 0, // write output over memory that won't be reused - 0 // don't copy output to memory - ) - - // Revert with reason given by AssetProxy if `transferFrom` call failed - if iszero(success) { - returndatacopy( - 0, // copy to memory at 0 - 0, // copy from return data at 0 - returndatasize() // copy all return data - ) - revert(0, returndatasize()) - } - } - - // Return if no `transferFrom` calls reverted - return(0, 0) - } - - // Revert if undefined function is called - revert(0, 0) - } - } - - /// @dev Gets the proxy id associated with the proxy address. - /// @return Proxy id. - function getProxyId() - external - pure - returns (bytes4) - { - return PROXY_ID; - } -} diff --git a/contracts/core/contracts/protocol/AssetProxy/mixins/MAuthorizable.sol b/contracts/core/contracts/protocol/AssetProxy/mixins/MAuthorizable.sol deleted file mode 100644 index fe77048ce..000000000 --- a/contracts/core/contracts/protocol/AssetProxy/mixins/MAuthorizable.sol +++ /dev/null @@ -1,41 +0,0 @@ -/* - - 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-interfaces/contracts/protocol/AssetProxy/IAuthorizable.sol"; - - -contract MAuthorizable is - IAuthorizable -{ - // Event logged when a new address is authorized. - event AuthorizedAddressAdded( - address indexed target, - address indexed caller - ); - - // Event logged when a currently authorized address is unauthorized. - event AuthorizedAddressRemoved( - address indexed target, - address indexed caller - ); - - /// @dev Only authorized addresses can invoke functions with this modifier. - modifier onlyAuthorized { revert(); _; } -} diff --git a/contracts/core/contracts/protocol/AssetProxyOwner/AssetProxyOwner.sol b/contracts/core/contracts/protocol/AssetProxyOwner/AssetProxyOwner.sol deleted file mode 100644 index bfc7b5a66..000000000 --- a/contracts/core/contracts/protocol/AssetProxyOwner/AssetProxyOwner.sol +++ /dev/null @@ -1,108 +0,0 @@ -/* - - 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-multisig/contracts/multisig/MultiSigWalletWithTimeLock.sol"; -import "@0x/contracts-utils/contracts/utils/LibBytes/LibBytes.sol"; - - -contract AssetProxyOwner is - MultiSigWalletWithTimeLock -{ - using LibBytes for bytes; - - event AssetProxyRegistration(address assetProxyContract, bool isRegistered); - - // Mapping of AssetProxy contract address => - // if this contract is allowed to call the AssetProxy's `removeAuthorizedAddressAtIndex` method without a time lock. - mapping (address => bool) public isAssetProxyRegistered; - - bytes4 constant internal REMOVE_AUTHORIZED_ADDRESS_AT_INDEX_SELECTOR = bytes4(keccak256("removeAuthorizedAddressAtIndex(address,uint256)")); - - /// @dev Function will revert if the transaction does not call `removeAuthorizedAddressAtIndex` - /// on an approved AssetProxy contract. - modifier validRemoveAuthorizedAddressAtIndexTx(uint256 transactionId) { - Transaction storage txn = transactions[transactionId]; - require( - isAssetProxyRegistered[txn.destination], - "UNREGISTERED_ASSET_PROXY" - ); - require( - txn.data.readBytes4(0) == REMOVE_AUTHORIZED_ADDRESS_AT_INDEX_SELECTOR, - "INVALID_FUNCTION_SELECTOR" - ); - _; - } - - /// @dev Contract constructor sets initial owners, required number of confirmations, - /// time lock, and list of AssetProxy addresses. - /// @param _owners List of initial owners. - /// @param _assetProxyContracts Array of AssetProxy contract addresses. - /// @param _required Number of required confirmations. - /// @param _secondsTimeLocked Duration needed after a transaction is confirmed and before it becomes executable, in seconds. - constructor ( - address[] memory _owners, - address[] memory _assetProxyContracts, - uint256 _required, - uint256 _secondsTimeLocked - ) - public - MultiSigWalletWithTimeLock(_owners, _required, _secondsTimeLocked) - { - for (uint256 i = 0; i < _assetProxyContracts.length; i++) { - address assetProxy = _assetProxyContracts[i]; - require( - assetProxy != address(0), - "INVALID_ASSET_PROXY" - ); - isAssetProxyRegistered[assetProxy] = true; - } - } - - /// @dev Registers or deregisters an AssetProxy to be able to execute - /// `removeAuthorizedAddressAtIndex` without a timelock. - /// @param assetProxyContract Address of AssetProxy contract. - /// @param isRegistered Status of approval for AssetProxy contract. - function registerAssetProxy(address assetProxyContract, bool isRegistered) - public - onlyWallet - notNull(assetProxyContract) - { - isAssetProxyRegistered[assetProxyContract] = isRegistered; - emit AssetProxyRegistration(assetProxyContract, isRegistered); - } - - /// @dev Allows execution of `removeAuthorizedAddressAtIndex` without time lock. - /// @param transactionId Transaction ID. - function executeRemoveAuthorizedAddressAtIndex(uint256 transactionId) - public - notExecuted(transactionId) - fullyConfirmed(transactionId) - validRemoveAuthorizedAddressAtIndexTx(transactionId) - { - Transaction storage txn = transactions[transactionId]; - txn.executed = true; - if (external_call(txn.destination, txn.value, txn.data.length, txn.data)) { - emit Execution(transactionId); - } else { - emit ExecutionFailure(transactionId); - txn.executed = false; - } - } -} diff --git a/contracts/core/contracts/protocol/Exchange/Exchange.sol b/contracts/core/contracts/protocol/Exchange/Exchange.sol deleted file mode 100644 index 65ca742ea..000000000 --- a/contracts/core/contracts/protocol/Exchange/Exchange.sol +++ /dev/null @@ -1,53 +0,0 @@ -/* - - 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 "@0x/contracts-libs/contracts/libs/LibConstants.sol"; -import "./MixinExchangeCore.sol"; -import "./MixinSignatureValidator.sol"; -import "./MixinWrapperFunctions.sol"; -import "./MixinAssetProxyDispatcher.sol"; -import "./MixinTransactions.sol"; -import "./MixinMatchOrders.sol"; - - -// solhint-disable no-empty-blocks -contract Exchange is - MixinExchangeCore, - MixinMatchOrders, - MixinSignatureValidator, - MixinTransactions, - MixinAssetProxyDispatcher, - MixinWrapperFunctions -{ - string constant public VERSION = "2.0.1-alpha"; - - // Mixins are instantiated in the order they are inherited - constructor (bytes memory _zrxAssetData) - public - LibConstants(_zrxAssetData) // @TODO: Remove when we deploy. - MixinExchangeCore() - MixinMatchOrders() - MixinSignatureValidator() - MixinTransactions() - MixinAssetProxyDispatcher() - MixinWrapperFunctions() - {} -} diff --git a/contracts/core/contracts/protocol/Exchange/MixinAssetProxyDispatcher.sol b/contracts/core/contracts/protocol/Exchange/MixinAssetProxyDispatcher.sol deleted file mode 100644 index 36ab39b45..000000000 --- a/contracts/core/contracts/protocol/Exchange/MixinAssetProxyDispatcher.sol +++ /dev/null @@ -1,174 +0,0 @@ -/* - - 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/Ownable/Ownable.sol"; -import "./mixins/MAssetProxyDispatcher.sol"; -import "@0x/contracts-interfaces/contracts/protocol/AssetProxy/IAssetProxy.sol"; - - -contract MixinAssetProxyDispatcher is - Ownable, - MAssetProxyDispatcher -{ - // Mapping from Asset Proxy Id's to their respective Asset Proxy - mapping (bytes4 => IAssetProxy) public assetProxies; - - /// @dev Registers an asset proxy to its asset proxy id. - /// Once an asset proxy is registered, it cannot be unregistered. - /// @param assetProxy Address of new asset proxy to register. - function registerAssetProxy(address assetProxy) - external - onlyOwner - { - IAssetProxy assetProxyContract = IAssetProxy(assetProxy); - - // Ensure that no asset proxy exists with current id. - bytes4 assetProxyId = assetProxyContract.getProxyId(); - address currentAssetProxy = assetProxies[assetProxyId]; - require( - currentAssetProxy == address(0), - "ASSET_PROXY_ALREADY_EXISTS" - ); - - // Add asset proxy and log registration. - assetProxies[assetProxyId] = assetProxyContract; - emit AssetProxyRegistered( - assetProxyId, - assetProxy - ); - } - - /// @dev Gets an asset proxy. - /// @param assetProxyId Id of the asset proxy. - /// @return The asset proxy registered to assetProxyId. Returns 0x0 if no proxy is registered. - function getAssetProxy(bytes4 assetProxyId) - external - view - returns (address) - { - return assetProxies[assetProxyId]; - } - - /// @dev Forwards arguments to assetProxy and calls `transferFrom`. Either succeeds or throws. - /// @param assetData Byte array encoded for the asset. - /// @param from Address to transfer token from. - /// @param to Address to transfer token to. - /// @param amount Amount of token to transfer. - function dispatchTransferFrom( - bytes memory assetData, - address from, - address to, - uint256 amount - ) - internal - { - // Do nothing if no amount should be transferred. - if (amount > 0 && from != to) { - // Ensure assetData length is valid - require( - assetData.length > 3, - "LENGTH_GREATER_THAN_3_REQUIRED" - ); - - // Lookup assetProxy. We do not use `LibBytes.readBytes4` for gas efficiency reasons. - bytes4 assetProxyId; - assembly { - assetProxyId := and(mload( - add(assetData, 32)), - 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000 - ) - } - address assetProxy = assetProxies[assetProxyId]; - - // Ensure that assetProxy exists - require( - assetProxy != address(0), - "ASSET_PROXY_DOES_NOT_EXIST" - ); - - // We construct calldata for the `assetProxy.transferFrom` ABI. - // The layout of this calldata is in the table below. - // - // | Area | Offset | Length | Contents | - // | -------- |--------|---------|-------------------------------------------- | - // | Header | 0 | 4 | function selector | - // | Params | | 4 * 32 | function parameters: | - // | | 4 | | 1. offset to assetData (*) | - // | | 36 | | 2. from | - // | | 68 | | 3. to | - // | | 100 | | 4. amount | - // | Data | | | assetData: | - // | | 132 | 32 | assetData Length | - // | | 164 | ** | assetData Contents | - - assembly { - /////// Setup State /////// - // `cdStart` is the start of the calldata for `assetProxy.transferFrom` (equal to free memory ptr). - let cdStart := mload(64) - // `dataAreaLength` is the total number of words needed to store `assetData` - // As-per the ABI spec, this value is padded up to the nearest multiple of 32, - // and includes 32-bytes for length. - let dataAreaLength := and(add(mload(assetData), 63), 0xFFFFFFFFFFFE0) - // `cdEnd` is the end of the calldata for `assetProxy.transferFrom`. - let cdEnd := add(cdStart, add(132, dataAreaLength)) - - - /////// Setup Header Area /////// - // This area holds the 4-byte `transferFromSelector`. - // bytes4(keccak256("transferFrom(bytes,address,address,uint256)")) = 0xa85e59e4 - mstore(cdStart, 0xa85e59e400000000000000000000000000000000000000000000000000000000) - - /////// Setup Params Area /////// - // Each parameter is padded to 32-bytes. The entire Params Area is 128 bytes. - // Notes: - // 1. The offset to `assetData` is the length of the Params Area (128 bytes). - // 2. A 20-byte mask is applied to addresses to zero-out the unused bytes. - mstore(add(cdStart, 4), 128) - mstore(add(cdStart, 36), and(from, 0xffffffffffffffffffffffffffffffffffffffff)) - mstore(add(cdStart, 68), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) - mstore(add(cdStart, 100), amount) - - /////// Setup Data Area /////// - // This area holds `assetData`. - let dataArea := add(cdStart, 132) - // solhint-disable-next-line no-empty-blocks - for {} lt(dataArea, cdEnd) {} { - mstore(dataArea, mload(assetData)) - dataArea := add(dataArea, 32) - assetData := add(assetData, 32) - } - - /////// Call `assetProxy.transferFrom` using the constructed calldata /////// - let success := call( - gas, // forward all gas - assetProxy, // call address of asset proxy - 0, // don't send any ETH - cdStart, // pointer to start of input - sub(cdEnd, cdStart), // length of input - cdStart, // write output over input - 512 // reserve 512 bytes for output - ) - if iszero(success) { - revert(cdStart, returndatasize()) - } - } - } - } -} diff --git a/contracts/core/contracts/protocol/Exchange/MixinExchangeCore.sol b/contracts/core/contracts/protocol/Exchange/MixinExchangeCore.sol deleted file mode 100644 index 68d6a3897..000000000 --- a/contracts/core/contracts/protocol/Exchange/MixinExchangeCore.sol +++ /dev/null @@ -1,529 +0,0 @@ -/* - - 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 "@0x/contracts-utils/contracts/utils/ReentrancyGuard/ReentrancyGuard.sol"; -import "@0x/contracts-libs/contracts/libs/LibConstants.sol"; -import "@0x/contracts-libs/contracts/libs/LibFillResults.sol"; -import "@0x/contracts-libs/contracts/libs/LibOrder.sol"; -import "@0x/contracts-libs/contracts/libs/LibMath.sol"; -import "./mixins/MExchangeCore.sol"; -import "./mixins/MSignatureValidator.sol"; -import "./mixins/MTransactions.sol"; -import "./mixins/MAssetProxyDispatcher.sol"; - - -contract MixinExchangeCore is - ReentrancyGuard, - LibConstants, - LibMath, - LibOrder, - LibFillResults, - MAssetProxyDispatcher, - MExchangeCore, - MSignatureValidator, - MTransactions -{ - // Mapping of orderHash => amount of takerAsset already bought by maker - mapping (bytes32 => uint256) public filled; - - // Mapping of orderHash => cancelled - mapping (bytes32 => bool) public cancelled; - - // Mapping of makerAddress => senderAddress => lowest salt an order can have in order to be fillable - // Orders with specified senderAddress and with a salt less than their epoch are considered cancelled - mapping (address => mapping (address => uint256)) public orderEpoch; - - /// @dev Cancels all orders created by makerAddress with a salt less than or equal to the targetOrderEpoch - /// and senderAddress equal to msg.sender (or null address if msg.sender == makerAddress). - /// @param targetOrderEpoch Orders created with a salt less or equal to this value will be cancelled. - function cancelOrdersUpTo(uint256 targetOrderEpoch) - external - nonReentrant - { - address makerAddress = getCurrentContextAddress(); - // If this function is called via `executeTransaction`, we only update the orderEpoch for the makerAddress/msg.sender combination. - // This allows external filter contracts to add rules to how orders are cancelled via this function. - address senderAddress = makerAddress == msg.sender ? address(0) : msg.sender; - - // orderEpoch is initialized to 0, so to cancelUpTo we need salt + 1 - uint256 newOrderEpoch = targetOrderEpoch + 1; - uint256 oldOrderEpoch = orderEpoch[makerAddress][senderAddress]; - - // Ensure orderEpoch is monotonically increasing - require( - newOrderEpoch > oldOrderEpoch, - "INVALID_NEW_ORDER_EPOCH" - ); - - // Update orderEpoch - orderEpoch[makerAddress][senderAddress] = newOrderEpoch; - emit CancelUpTo( - makerAddress, - senderAddress, - newOrderEpoch - ); - } - - /// @dev Fills the input order. - /// @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 Amounts filled and fees paid by maker and taker. - function fillOrder( - Order memory order, - uint256 takerAssetFillAmount, - bytes memory signature - ) - public - nonReentrant - returns (FillResults memory fillResults) - { - fillResults = fillOrderInternal( - order, - takerAssetFillAmount, - signature - ); - return fillResults; - } - - /// @dev After calling, the order can not be filled anymore. - /// Throws if order is invalid or sender does not have permission to cancel. - /// @param order Order to cancel. Order must be OrderStatus.FILLABLE. - function cancelOrder(Order memory order) - public - nonReentrant - { - cancelOrderInternal(order); - } - - /// @dev Gets information about an order: status, hash, and amount filled. - /// @param order Order to gather information on. - /// @return OrderInfo Information about the order and its state. - /// See LibOrder.OrderInfo for a complete description. - function getOrderInfo(Order memory order) - public - view - returns (OrderInfo memory orderInfo) - { - // Compute the order hash - orderInfo.orderHash = getOrderHash(order); - - // Fetch filled amount - orderInfo.orderTakerAssetFilledAmount = filled[orderInfo.orderHash]; - - // If order.makerAssetAmount is zero, we also reject the order. - // While the Exchange contract handles them correctly, they create - // edge cases in the supporting infrastructure because they have - // an 'infinite' price when computed by a simple division. - if (order.makerAssetAmount == 0) { - orderInfo.orderStatus = uint8(OrderStatus.INVALID_MAKER_ASSET_AMOUNT); - return orderInfo; - } - - // If order.takerAssetAmount is zero, then the order will always - // be considered filled because 0 == takerAssetAmount == orderTakerAssetFilledAmount - // Instead of distinguishing between unfilled and filled zero taker - // amount orders, we choose not to support them. - if (order.takerAssetAmount == 0) { - orderInfo.orderStatus = uint8(OrderStatus.INVALID_TAKER_ASSET_AMOUNT); - return orderInfo; - } - - // Validate order availability - if (orderInfo.orderTakerAssetFilledAmount >= order.takerAssetAmount) { - orderInfo.orderStatus = uint8(OrderStatus.FULLY_FILLED); - return orderInfo; - } - - // Validate order expiration - // solhint-disable-next-line not-rely-on-time - if (block.timestamp >= order.expirationTimeSeconds) { - orderInfo.orderStatus = uint8(OrderStatus.EXPIRED); - return orderInfo; - } - - // Check if order has been cancelled - if (cancelled[orderInfo.orderHash]) { - orderInfo.orderStatus = uint8(OrderStatus.CANCELLED); - return orderInfo; - } - if (orderEpoch[order.makerAddress][order.senderAddress] > order.salt) { - orderInfo.orderStatus = uint8(OrderStatus.CANCELLED); - return orderInfo; - } - - // All other statuses are ruled out: order is Fillable - orderInfo.orderStatus = uint8(OrderStatus.FILLABLE); - return orderInfo; - } - - /// @dev Fills the input order. - /// @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 Amounts filled and fees paid by maker and taker. - function fillOrderInternal( - Order memory order, - uint256 takerAssetFillAmount, - bytes memory signature - ) - internal - returns (FillResults memory fillResults) - { - // Fetch order info - OrderInfo memory orderInfo = getOrderInfo(order); - - // Fetch taker address - address takerAddress = getCurrentContextAddress(); - - // Assert that the order is fillable by taker - assertFillableOrder( - order, - orderInfo, - takerAddress, - signature - ); - - // Get amount of takerAsset to fill - uint256 remainingTakerAssetAmount = safeSub(order.takerAssetAmount, orderInfo.orderTakerAssetFilledAmount); - uint256 takerAssetFilledAmount = min256(takerAssetFillAmount, remainingTakerAssetAmount); - - // Validate context - assertValidFill( - order, - orderInfo, - takerAssetFillAmount, - takerAssetFilledAmount, - fillResults.makerAssetFilledAmount - ); - - // Compute proportional fill amounts - fillResults = calculateFillResults(order, takerAssetFilledAmount); - - // Update exchange internal state - updateFilledState( - order, - takerAddress, - orderInfo.orderHash, - orderInfo.orderTakerAssetFilledAmount, - fillResults - ); - - // Settle order - settleOrder( - order, - takerAddress, - fillResults - ); - - return fillResults; - } - - /// @dev After calling, the order can not be filled anymore. - /// Throws if order is invalid or sender does not have permission to cancel. - /// @param order Order to cancel. Order must be OrderStatus.FILLABLE. - function cancelOrderInternal(Order memory order) - internal - { - // Fetch current order status - OrderInfo memory orderInfo = getOrderInfo(order); - - // Validate context - assertValidCancel(order, orderInfo); - - // Perform cancel - updateCancelledState(order, orderInfo.orderHash); - } - - /// @dev Updates state with results of a fill order. - /// @param order that was filled. - /// @param takerAddress Address of taker who filled the order. - /// @param orderTakerAssetFilledAmount Amount of order already filled. - function updateFilledState( - Order memory order, - address takerAddress, - bytes32 orderHash, - uint256 orderTakerAssetFilledAmount, - FillResults memory fillResults - ) - internal - { - // Update state - filled[orderHash] = safeAdd(orderTakerAssetFilledAmount, fillResults.takerAssetFilledAmount); - - // Log order - emit Fill( - order.makerAddress, - order.feeRecipientAddress, - takerAddress, - msg.sender, - fillResults.makerAssetFilledAmount, - fillResults.takerAssetFilledAmount, - fillResults.makerFeePaid, - fillResults.takerFeePaid, - orderHash, - order.makerAssetData, - order.takerAssetData - ); - } - - /// @dev Updates state with results of cancelling an order. - /// State is only updated if the order is currently fillable. - /// Otherwise, updating state would have no effect. - /// @param order that was cancelled. - /// @param orderHash Hash of order that was cancelled. - function updateCancelledState( - Order memory order, - bytes32 orderHash - ) - internal - { - // Perform cancel - cancelled[orderHash] = true; - - // Log cancel - emit Cancel( - order.makerAddress, - order.feeRecipientAddress, - msg.sender, - orderHash, - order.makerAssetData, - order.takerAssetData - ); - } - - /// @dev Validates context for fillOrder. Succeeds or throws. - /// @param order to be filled. - /// @param orderInfo OrderStatus, orderHash, and amount already filled of order. - /// @param takerAddress Address of order taker. - /// @param signature Proof that the orders was created by its maker. - function assertFillableOrder( - Order memory order, - OrderInfo memory orderInfo, - address takerAddress, - bytes memory signature - ) - internal - view - { - // An order can only be filled if its status is FILLABLE. - require( - orderInfo.orderStatus == uint8(OrderStatus.FILLABLE), - "ORDER_UNFILLABLE" - ); - - // Validate sender is allowed to fill this order - if (order.senderAddress != address(0)) { - require( - order.senderAddress == msg.sender, - "INVALID_SENDER" - ); - } - - // Validate taker is allowed to fill this order - if (order.takerAddress != address(0)) { - require( - order.takerAddress == takerAddress, - "INVALID_TAKER" - ); - } - - // Validate Maker signature (check only if first time seen) - if (orderInfo.orderTakerAssetFilledAmount == 0) { - require( - isValidSignature( - orderInfo.orderHash, - order.makerAddress, - signature - ), - "INVALID_ORDER_SIGNATURE" - ); - } - } - - /// @dev Validates context for fillOrder. Succeeds or throws. - /// @param order to be filled. - /// @param orderInfo OrderStatus, orderHash, and amount already filled of order. - /// @param takerAssetFillAmount Desired amount of order to fill by taker. - /// @param takerAssetFilledAmount Amount of takerAsset that will be filled. - /// @param makerAssetFilledAmount Amount of makerAsset that will be transfered. - function assertValidFill( - Order memory order, - OrderInfo memory orderInfo, - uint256 takerAssetFillAmount, // TODO: use FillResults - uint256 takerAssetFilledAmount, - uint256 makerAssetFilledAmount - ) - internal - view - { - // Revert if fill amount is invalid - // TODO: reconsider necessity for v2.1 - require( - takerAssetFillAmount != 0, - "INVALID_TAKER_AMOUNT" - ); - - // Make sure taker does not pay more than desired amount - // NOTE: This assertion should never fail, it is here - // as an extra defence against potential bugs. - require( - takerAssetFilledAmount <= takerAssetFillAmount, - "TAKER_OVERPAY" - ); - - // Make sure order is not overfilled - // NOTE: This assertion should never fail, it is here - // as an extra defence against potential bugs. - require( - safeAdd(orderInfo.orderTakerAssetFilledAmount, takerAssetFilledAmount) <= order.takerAssetAmount, - "ORDER_OVERFILL" - ); - - // Make sure order is filled at acceptable price. - // The order has an implied price from the makers perspective: - // order price = order.makerAssetAmount / order.takerAssetAmount - // i.e. the number of makerAsset maker is paying per takerAsset. The - // maker is guaranteed to get this price or a better (lower) one. The - // actual price maker is getting in this fill is: - // fill price = makerAssetFilledAmount / takerAssetFilledAmount - // We need `fill price <= order price` for the fill to be fair to maker. - // This amounts to: - // makerAssetFilledAmount order.makerAssetAmount - // ------------------------ <= ----------------------- - // takerAssetFilledAmount order.takerAssetAmount - // or, equivalently: - // makerAssetFilledAmount * order.takerAssetAmount <= - // order.makerAssetAmount * takerAssetFilledAmount - // NOTE: This assertion should never fail, it is here - // as an extra defence against potential bugs. - require( - safeMul(makerAssetFilledAmount, order.takerAssetAmount) - <= - safeMul(order.makerAssetAmount, takerAssetFilledAmount), - "INVALID_FILL_PRICE" - ); - } - - /// @dev Validates context for cancelOrder. Succeeds or throws. - /// @param order to be cancelled. - /// @param orderInfo OrderStatus, orderHash, and amount already filled of order. - function assertValidCancel( - Order memory order, - OrderInfo memory orderInfo - ) - internal - view - { - // Ensure order is valid - // An order can only be cancelled if its status is FILLABLE. - require( - orderInfo.orderStatus == uint8(OrderStatus.FILLABLE), - "ORDER_UNFILLABLE" - ); - - // Validate sender is allowed to cancel this order - if (order.senderAddress != address(0)) { - require( - order.senderAddress == msg.sender, - "INVALID_SENDER" - ); - } - - // Validate transaction signed by maker - address makerAddress = getCurrentContextAddress(); - require( - order.makerAddress == makerAddress, - "INVALID_MAKER" - ); - } - - /// @dev Calculates amounts filled and fees paid by maker and taker. - /// @param order to be filled. - /// @param takerAssetFilledAmount Amount of takerAsset that will be filled. - /// @return fillResults Amounts filled and fees paid by maker and taker. - function calculateFillResults( - Order memory order, - uint256 takerAssetFilledAmount - ) - internal - pure - returns (FillResults memory fillResults) - { - // Compute proportional transfer amounts - fillResults.takerAssetFilledAmount = takerAssetFilledAmount; - fillResults.makerAssetFilledAmount = safeGetPartialAmountFloor( - takerAssetFilledAmount, - order.takerAssetAmount, - order.makerAssetAmount - ); - fillResults.makerFeePaid = safeGetPartialAmountFloor( - fillResults.makerAssetFilledAmount, - order.makerAssetAmount, - order.makerFee - ); - fillResults.takerFeePaid = safeGetPartialAmountFloor( - takerAssetFilledAmount, - order.takerAssetAmount, - order.takerFee - ); - - return fillResults; - } - - /// @dev Settles an order by transferring assets between counterparties. - /// @param order Order struct containing order specifications. - /// @param takerAddress Address selling takerAsset and buying makerAsset. - /// @param fillResults Amounts to be filled and fees paid by maker and taker. - function settleOrder( - LibOrder.Order memory order, - address takerAddress, - LibFillResults.FillResults memory fillResults - ) - private - { - bytes memory zrxAssetData = ZRX_ASSET_DATA; - dispatchTransferFrom( - order.makerAssetData, - order.makerAddress, - takerAddress, - fillResults.makerAssetFilledAmount - ); - dispatchTransferFrom( - order.takerAssetData, - takerAddress, - order.makerAddress, - fillResults.takerAssetFilledAmount - ); - dispatchTransferFrom( - zrxAssetData, - order.makerAddress, - order.feeRecipientAddress, - fillResults.makerFeePaid - ); - dispatchTransferFrom( - zrxAssetData, - takerAddress, - order.feeRecipientAddress, - fillResults.takerFeePaid - ); - } -} diff --git a/contracts/core/contracts/protocol/Exchange/MixinMatchOrders.sol b/contracts/core/contracts/protocol/Exchange/MixinMatchOrders.sol deleted file mode 100644 index fc6d73482..000000000 --- a/contracts/core/contracts/protocol/Exchange/MixinMatchOrders.sol +++ /dev/null @@ -1,335 +0,0 @@ -/* - 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 "@0x/contracts-utils/contracts/utils/ReentrancyGuard/ReentrancyGuard.sol"; -import "@0x/contracts-libs/contracts/libs/LibConstants.sol"; -import "@0x/contracts-libs/contracts/libs/LibMath.sol"; -import "@0x/contracts-libs/contracts/libs/LibOrder.sol"; -import "@0x/contracts-libs/contracts/libs/LibFillResults.sol"; -import "./mixins/MExchangeCore.sol"; -import "./mixins/MMatchOrders.sol"; -import "./mixins/MTransactions.sol"; -import "./mixins/MAssetProxyDispatcher.sol"; - - -contract MixinMatchOrders is - ReentrancyGuard, - LibConstants, - LibMath, - MAssetProxyDispatcher, - MExchangeCore, - MMatchOrders, - MTransactions -{ - /// @dev Match two complementary orders that have a profitable spread. - /// Each order is filled at their respective price point. However, the calculations are - /// carried out as though the orders are both being filled at the right order's price point. - /// The profit made by the left order goes to the taker (who matched the two orders). - /// @param leftOrder First order to match. - /// @param rightOrder Second order to match. - /// @param leftSignature Proof that order was created by the left maker. - /// @param rightSignature Proof that order was created by the right maker. - /// @return matchedFillResults Amounts filled and fees paid by maker and taker of matched orders. - function matchOrders( - LibOrder.Order memory leftOrder, - LibOrder.Order memory rightOrder, - bytes memory leftSignature, - bytes memory rightSignature - ) - public - nonReentrant - returns (LibFillResults.MatchedFillResults memory matchedFillResults) - { - // We assume that rightOrder.takerAssetData == leftOrder.makerAssetData and rightOrder.makerAssetData == leftOrder.takerAssetData. - // If this assumption isn't true, the match will fail at signature validation. - rightOrder.makerAssetData = leftOrder.takerAssetData; - rightOrder.takerAssetData = leftOrder.makerAssetData; - - // Get left & right order info - LibOrder.OrderInfo memory leftOrderInfo = getOrderInfo(leftOrder); - LibOrder.OrderInfo memory rightOrderInfo = getOrderInfo(rightOrder); - - // Fetch taker address - address takerAddress = getCurrentContextAddress(); - - // Either our context is valid or we revert - assertFillableOrder( - leftOrder, - leftOrderInfo, - takerAddress, - leftSignature - ); - assertFillableOrder( - rightOrder, - rightOrderInfo, - takerAddress, - rightSignature - ); - assertValidMatch(leftOrder, rightOrder); - - // Compute proportional fill amounts - matchedFillResults = calculateMatchedFillResults( - leftOrder, - rightOrder, - leftOrderInfo.orderTakerAssetFilledAmount, - rightOrderInfo.orderTakerAssetFilledAmount - ); - - // Validate fill contexts - assertValidFill( - leftOrder, - leftOrderInfo, - matchedFillResults.left.takerAssetFilledAmount, - matchedFillResults.left.takerAssetFilledAmount, - matchedFillResults.left.makerAssetFilledAmount - ); - assertValidFill( - rightOrder, - rightOrderInfo, - matchedFillResults.right.takerAssetFilledAmount, - matchedFillResults.right.takerAssetFilledAmount, - matchedFillResults.right.makerAssetFilledAmount - ); - - // Update exchange state - updateFilledState( - leftOrder, - takerAddress, - leftOrderInfo.orderHash, - leftOrderInfo.orderTakerAssetFilledAmount, - matchedFillResults.left - ); - updateFilledState( - rightOrder, - takerAddress, - rightOrderInfo.orderHash, - rightOrderInfo.orderTakerAssetFilledAmount, - matchedFillResults.right - ); - - // Settle matched orders. Succeeds or throws. - settleMatchedOrders( - leftOrder, - rightOrder, - takerAddress, - matchedFillResults - ); - - return matchedFillResults; - } - - /// @dev Validates context for matchOrders. Succeeds or throws. - /// @param leftOrder First order to match. - /// @param rightOrder Second order to match. - function assertValidMatch( - LibOrder.Order memory leftOrder, - LibOrder.Order memory rightOrder - ) - internal - pure - { - // Make sure there is a profitable spread. - // There is a profitable spread iff the cost per unit bought (OrderA.MakerAmount/OrderA.TakerAmount) for each order is greater - // than the profit per unit sold of the matched order (OrderB.TakerAmount/OrderB.MakerAmount). - // This is satisfied by the equations below: - // <leftOrder.makerAssetAmount> / <leftOrder.takerAssetAmount> >= <rightOrder.takerAssetAmount> / <rightOrder.makerAssetAmount> - // AND - // <rightOrder.makerAssetAmount> / <rightOrder.takerAssetAmount> >= <leftOrder.takerAssetAmount> / <leftOrder.makerAssetAmount> - // These equations can be combined to get the following: - require( - safeMul(leftOrder.makerAssetAmount, rightOrder.makerAssetAmount) >= - safeMul(leftOrder.takerAssetAmount, rightOrder.takerAssetAmount), - "NEGATIVE_SPREAD_REQUIRED" - ); - } - - /// @dev Calculates fill amounts for the matched orders. - /// Each order is filled at their respective price point. However, the calculations are - /// carried out as though the orders are both being filled at the right order's price point. - /// The profit made by the leftOrder order goes to the taker (who matched the two orders). - /// @param leftOrder First order to match. - /// @param rightOrder Second order to match. - /// @param leftOrderTakerAssetFilledAmount Amount of left order already filled. - /// @param rightOrderTakerAssetFilledAmount Amount of right order already filled. - /// @param matchedFillResults Amounts to fill and fees to pay by maker and taker of matched orders. - function calculateMatchedFillResults( - LibOrder.Order memory leftOrder, - LibOrder.Order memory rightOrder, - uint256 leftOrderTakerAssetFilledAmount, - uint256 rightOrderTakerAssetFilledAmount - ) - internal - pure - returns (LibFillResults.MatchedFillResults memory matchedFillResults) - { - // Derive maker asset amounts for left & right orders, given store taker assert amounts - uint256 leftTakerAssetAmountRemaining = safeSub(leftOrder.takerAssetAmount, leftOrderTakerAssetFilledAmount); - uint256 leftMakerAssetAmountRemaining = safeGetPartialAmountFloor( - leftOrder.makerAssetAmount, - leftOrder.takerAssetAmount, - leftTakerAssetAmountRemaining - ); - uint256 rightTakerAssetAmountRemaining = safeSub(rightOrder.takerAssetAmount, rightOrderTakerAssetFilledAmount); - uint256 rightMakerAssetAmountRemaining = safeGetPartialAmountFloor( - rightOrder.makerAssetAmount, - rightOrder.takerAssetAmount, - rightTakerAssetAmountRemaining - ); - - // Calculate fill results for maker and taker assets: at least one order will be fully filled. - // The maximum amount the left maker can buy is `leftTakerAssetAmountRemaining` - // The maximum amount the right maker can sell is `rightMakerAssetAmountRemaining` - // We have two distinct cases for calculating the fill results: - // Case 1. - // If the left maker can buy more than the right maker can sell, then only the right order is fully filled. - // If the left maker can buy exactly what the right maker can sell, then both orders are fully filled. - // Case 2. - // If the left maker cannot buy more than the right maker can sell, then only the left order is fully filled. - if (leftTakerAssetAmountRemaining >= rightMakerAssetAmountRemaining) { - // Case 1: Right order is fully filled - matchedFillResults.right.makerAssetFilledAmount = rightMakerAssetAmountRemaining; - matchedFillResults.right.takerAssetFilledAmount = rightTakerAssetAmountRemaining; - matchedFillResults.left.takerAssetFilledAmount = matchedFillResults.right.makerAssetFilledAmount; - // Round down to ensure the maker's exchange rate does not exceed the price specified by the order. - // We favor the maker when the exchange rate must be rounded. - matchedFillResults.left.makerAssetFilledAmount = safeGetPartialAmountFloor( - leftOrder.makerAssetAmount, - leftOrder.takerAssetAmount, - matchedFillResults.left.takerAssetFilledAmount - ); - } else { - // Case 2: Left order is fully filled - matchedFillResults.left.makerAssetFilledAmount = leftMakerAssetAmountRemaining; - matchedFillResults.left.takerAssetFilledAmount = leftTakerAssetAmountRemaining; - matchedFillResults.right.makerAssetFilledAmount = matchedFillResults.left.takerAssetFilledAmount; - // Round up to ensure the maker's exchange rate does not exceed the price specified by the order. - // We favor the maker when the exchange rate must be rounded. - matchedFillResults.right.takerAssetFilledAmount = safeGetPartialAmountCeil( - rightOrder.takerAssetAmount, - rightOrder.makerAssetAmount, - matchedFillResults.right.makerAssetFilledAmount - ); - } - - // Calculate amount given to taker - matchedFillResults.leftMakerAssetSpreadAmount = safeSub( - matchedFillResults.left.makerAssetFilledAmount, - matchedFillResults.right.takerAssetFilledAmount - ); - - // Compute fees for left order - matchedFillResults.left.makerFeePaid = safeGetPartialAmountFloor( - matchedFillResults.left.makerAssetFilledAmount, - leftOrder.makerAssetAmount, - leftOrder.makerFee - ); - matchedFillResults.left.takerFeePaid = safeGetPartialAmountFloor( - matchedFillResults.left.takerAssetFilledAmount, - leftOrder.takerAssetAmount, - leftOrder.takerFee - ); - - // Compute fees for right order - matchedFillResults.right.makerFeePaid = safeGetPartialAmountFloor( - matchedFillResults.right.makerAssetFilledAmount, - rightOrder.makerAssetAmount, - rightOrder.makerFee - ); - matchedFillResults.right.takerFeePaid = safeGetPartialAmountFloor( - matchedFillResults.right.takerAssetFilledAmount, - rightOrder.takerAssetAmount, - rightOrder.takerFee - ); - - // Return fill results - return matchedFillResults; - } - - /// @dev Settles matched order by transferring appropriate funds between order makers, taker, and fee recipient. - /// @param leftOrder First matched order. - /// @param rightOrder Second matched order. - /// @param takerAddress Address that matched the orders. The taker receives the spread between orders as profit. - /// @param matchedFillResults Struct holding amounts to transfer between makers, taker, and fee recipients. - function settleMatchedOrders( - LibOrder.Order memory leftOrder, - LibOrder.Order memory rightOrder, - address takerAddress, - LibFillResults.MatchedFillResults memory matchedFillResults - ) - private - { - bytes memory zrxAssetData = ZRX_ASSET_DATA; - // Order makers and taker - dispatchTransferFrom( - leftOrder.makerAssetData, - leftOrder.makerAddress, - rightOrder.makerAddress, - matchedFillResults.right.takerAssetFilledAmount - ); - dispatchTransferFrom( - rightOrder.makerAssetData, - rightOrder.makerAddress, - leftOrder.makerAddress, - matchedFillResults.left.takerAssetFilledAmount - ); - dispatchTransferFrom( - leftOrder.makerAssetData, - leftOrder.makerAddress, - takerAddress, - matchedFillResults.leftMakerAssetSpreadAmount - ); - - // Maker fees - dispatchTransferFrom( - zrxAssetData, - leftOrder.makerAddress, - leftOrder.feeRecipientAddress, - matchedFillResults.left.makerFeePaid - ); - dispatchTransferFrom( - zrxAssetData, - rightOrder.makerAddress, - rightOrder.feeRecipientAddress, - matchedFillResults.right.makerFeePaid - ); - - // Taker fees - if (leftOrder.feeRecipientAddress == rightOrder.feeRecipientAddress) { - dispatchTransferFrom( - zrxAssetData, - takerAddress, - leftOrder.feeRecipientAddress, - safeAdd( - matchedFillResults.left.takerFeePaid, - matchedFillResults.right.takerFeePaid - ) - ); - } else { - dispatchTransferFrom( - zrxAssetData, - takerAddress, - leftOrder.feeRecipientAddress, - matchedFillResults.left.takerFeePaid - ); - dispatchTransferFrom( - zrxAssetData, - takerAddress, - rightOrder.feeRecipientAddress, - matchedFillResults.right.takerFeePaid - ); - } - } -} diff --git a/contracts/core/contracts/protocol/Exchange/MixinSignatureValidator.sol b/contracts/core/contracts/protocol/Exchange/MixinSignatureValidator.sol deleted file mode 100644 index 80b4c0755..000000000 --- a/contracts/core/contracts/protocol/Exchange/MixinSignatureValidator.sol +++ /dev/null @@ -1,324 +0,0 @@ -/* - - 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/LibBytes/LibBytes.sol"; -import "@0x/contracts-utils/contracts/utils/ReentrancyGuard/ReentrancyGuard.sol"; -import "./mixins/MSignatureValidator.sol"; -import "./mixins/MTransactions.sol"; -import "@0x/contracts-interfaces/contracts/protocol/Exchange/IWallet.sol"; -import "@0x/contracts-interfaces/contracts/protocol/Exchange/IValidator.sol"; - - -contract MixinSignatureValidator is - ReentrancyGuard, - MSignatureValidator, - MTransactions -{ - using LibBytes for bytes; - - // Mapping of hash => signer => signed - mapping (bytes32 => mapping (address => bool)) public preSigned; - - // Mapping of signer => validator => approved - mapping (address => mapping (address => bool)) public allowedValidators; - - /// @dev Approves a hash on-chain using any valid signature type. - /// After presigning a hash, the preSign signature type will become valid for that hash and signer. - /// @param signerAddress Address that should have signed the given hash. - /// @param signature Proof that the hash has been signed by signer. - function preSign( - bytes32 hash, - address signerAddress, - bytes signature - ) - external - { - if (signerAddress != msg.sender) { - require( - isValidSignature( - hash, - signerAddress, - signature - ), - "INVALID_SIGNATURE" - ); - } - preSigned[hash][signerAddress] = true; - } - - /// @dev Approves/unnapproves a Validator contract to verify signatures on signer's behalf. - /// @param validatorAddress Address of Validator contract. - /// @param approval Approval or disapproval of Validator contract. - function setSignatureValidatorApproval( - address validatorAddress, - bool approval - ) - external - nonReentrant - { - address signerAddress = getCurrentContextAddress(); - allowedValidators[signerAddress][validatorAddress] = approval; - emit SignatureValidatorApproval( - signerAddress, - validatorAddress, - approval - ); - } - - /// @dev Verifies that a hash has been signed by the given signer. - /// @param hash Any 32 byte hash. - /// @param signerAddress Address that should have signed the given hash. - /// @param signature Proof that the hash has been signed by signer. - /// @return True if the address recovered from the provided signature matches the input signer address. - function isValidSignature( - bytes32 hash, - address signerAddress, - bytes memory signature - ) - public - view - returns (bool isValid) - { - require( - signature.length > 0, - "LENGTH_GREATER_THAN_0_REQUIRED" - ); - - // Pop last byte off of signature byte array. - uint8 signatureTypeRaw = uint8(signature.popLastByte()); - - // Ensure signature is supported - require( - signatureTypeRaw < uint8(SignatureType.NSignatureTypes), - "SIGNATURE_UNSUPPORTED" - ); - - SignatureType signatureType = SignatureType(signatureTypeRaw); - - // Variables are not scoped in Solidity. - uint8 v; - bytes32 r; - bytes32 s; - address recovered; - - // Always illegal signature. - // This is always an implicit option since a signer can create a - // signature array with invalid type or length. We may as well make - // it an explicit option. This aids testing and analysis. It is - // also the initialization value for the enum type. - if (signatureType == SignatureType.Illegal) { - revert("SIGNATURE_ILLEGAL"); - - // Always invalid signature. - // Like Illegal, this is always implicitly available and therefore - // offered explicitly. It can be implicitly created by providing - // a correctly formatted but incorrect signature. - } else if (signatureType == SignatureType.Invalid) { - require( - signature.length == 0, - "LENGTH_0_REQUIRED" - ); - isValid = false; - return isValid; - - // Signature using EIP712 - } else if (signatureType == SignatureType.EIP712) { - require( - signature.length == 65, - "LENGTH_65_REQUIRED" - ); - v = uint8(signature[0]); - r = signature.readBytes32(1); - s = signature.readBytes32(33); - recovered = ecrecover( - hash, - v, - r, - s - ); - isValid = signerAddress == recovered; - return isValid; - - // Signed using web3.eth_sign - } else if (signatureType == SignatureType.EthSign) { - require( - signature.length == 65, - "LENGTH_65_REQUIRED" - ); - v = uint8(signature[0]); - r = signature.readBytes32(1); - s = signature.readBytes32(33); - recovered = ecrecover( - keccak256(abi.encodePacked( - "\x19Ethereum Signed Message:\n32", - hash - )), - v, - r, - s - ); - isValid = signerAddress == recovered; - return isValid; - - // Signature verified by wallet contract. - // If used with an order, the maker of the order is the wallet contract. - } else if (signatureType == SignatureType.Wallet) { - isValid = isValidWalletSignature( - hash, - signerAddress, - signature - ); - return isValid; - - // Signature verified by validator contract. - // If used with an order, the maker of the order can still be an EOA. - // A signature using this type should be encoded as: - // | Offset | Length | Contents | - // | 0x00 | x | Signature to validate | - // | 0x00 + x | 20 | Address of validator contract | - // | 0x14 + x | 1 | Signature type is always "\x06" | - } else if (signatureType == SignatureType.Validator) { - // Pop last 20 bytes off of signature byte array. - address validatorAddress = signature.popLast20Bytes(); - - // Ensure signer has approved validator. - if (!allowedValidators[signerAddress][validatorAddress]) { - return false; - } - isValid = isValidValidatorSignature( - validatorAddress, - hash, - signerAddress, - signature - ); - return isValid; - - // Signer signed hash previously using the preSign function. - } else if (signatureType == SignatureType.PreSigned) { - isValid = preSigned[hash][signerAddress]; - return isValid; - } - - // Anything else is illegal (We do not return false because - // the signature may actually be valid, just not in a format - // that we currently support. In this case returning false - // may lead the caller to incorrectly believe that the - // signature was invalid.) - revert("SIGNATURE_UNSUPPORTED"); - } - - /// @dev Verifies signature using logic defined by Wallet contract. - /// @param hash Any 32 byte hash. - /// @param walletAddress Address that should have signed the given hash - /// and defines its own signature verification method. - /// @param signature Proof that the hash has been signed by signer. - /// @return True if signature is valid for given wallet.. - function isValidWalletSignature( - bytes32 hash, - address walletAddress, - bytes signature - ) - internal - view - returns (bool isValid) - { - bytes memory callData = abi.encodeWithSelector( - IWallet(walletAddress).isValidSignature.selector, - hash, - signature - ); - assembly { - let cdStart := add(callData, 32) - let success := staticcall( - gas, // forward all gas - walletAddress, // address of Wallet contract - cdStart, // pointer to start of input - mload(callData), // length of input - cdStart, // write output over input - 32 // output size is 32 bytes - ) - - switch success - case 0 { - // Revert with `Error("WALLET_ERROR")` - mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000) - mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000) - mstore(64, 0x0000000c57414c4c45545f4552524f5200000000000000000000000000000000) - mstore(96, 0) - revert(0, 100) - } - case 1 { - // Signature is valid if call did not revert and returned true - isValid := mload(cdStart) - } - } - return isValid; - } - - /// @dev Verifies signature using logic defined by Validator contract. - /// @param validatorAddress Address of validator contract. - /// @param hash Any 32 byte hash. - /// @param signerAddress Address that should have signed the given hash. - /// @param signature Proof that the hash has been signed by signer. - /// @return True if the address recovered from the provided signature matches the input signer address. - function isValidValidatorSignature( - address validatorAddress, - bytes32 hash, - address signerAddress, - bytes signature - ) - internal - view - returns (bool isValid) - { - bytes memory callData = abi.encodeWithSelector( - IValidator(signerAddress).isValidSignature.selector, - hash, - signerAddress, - signature - ); - assembly { - let cdStart := add(callData, 32) - let success := staticcall( - gas, // forward all gas - validatorAddress, // address of Validator contract - cdStart, // pointer to start of input - mload(callData), // length of input - cdStart, // write output over input - 32 // output size is 32 bytes - ) - - switch success - case 0 { - // Revert with `Error("VALIDATOR_ERROR")` - mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000) - mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000) - mstore(64, 0x0000000f56414c494441544f525f4552524f5200000000000000000000000000) - mstore(96, 0) - revert(0, 100) - } - case 1 { - // Signature is valid if call did not revert and returned true - isValid := mload(cdStart) - } - } - return isValid; - } -} diff --git a/contracts/core/contracts/protocol/Exchange/MixinTransactions.sol b/contracts/core/contracts/protocol/Exchange/MixinTransactions.sol deleted file mode 100644 index 87c614382..000000000 --- a/contracts/core/contracts/protocol/Exchange/MixinTransactions.sol +++ /dev/null @@ -1,152 +0,0 @@ -/* - - 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-libs/contracts/libs/LibExchangeErrors.sol"; -import "./mixins/MSignatureValidator.sol"; -import "./mixins/MTransactions.sol"; -import "@0x/contracts-libs/contracts/libs/LibEIP712.sol"; - - -contract MixinTransactions is - LibEIP712, - MSignatureValidator, - MTransactions -{ - // Mapping of transaction hash => executed - // This prevents transactions from being executed more than once. - mapping (bytes32 => bool) public transactions; - - // Address of current transaction signer - address public currentContextAddress; - - /// @dev Executes an exchange method call in the context of signer. - /// @param salt Arbitrary number to ensure uniqueness of transaction hash. - /// @param signerAddress Address of transaction signer. - /// @param data AbiV2 encoded calldata. - /// @param signature Proof of signer transaction by signer. - function executeTransaction( - uint256 salt, - address signerAddress, - bytes data, - bytes signature - ) - external - { - // Prevent reentrancy - require( - currentContextAddress == address(0), - "REENTRANCY_ILLEGAL" - ); - - bytes32 transactionHash = hashEIP712Message(hashZeroExTransaction( - salt, - signerAddress, - data - )); - - // Validate transaction has not been executed - require( - !transactions[transactionHash], - "INVALID_TX_HASH" - ); - - // Transaction always valid if signer is sender of transaction - if (signerAddress != msg.sender) { - // Validate signature - require( - isValidSignature( - transactionHash, - signerAddress, - signature - ), - "INVALID_TX_SIGNATURE" - ); - - // Set the current transaction signer - currentContextAddress = signerAddress; - } - - // Execute transaction - transactions[transactionHash] = true; - require( - address(this).delegatecall(data), - "FAILED_EXECUTION" - ); - - // Reset current transaction signer if it was previously updated - if (signerAddress != msg.sender) { - currentContextAddress = address(0); - } - } - - /// @dev Calculates EIP712 hash of the Transaction. - /// @param salt Arbitrary number to ensure uniqueness of transaction hash. - /// @param signerAddress Address of transaction signer. - /// @param data AbiV2 encoded calldata. - /// @return EIP712 hash of the Transaction. - function hashZeroExTransaction( - uint256 salt, - address signerAddress, - bytes memory data - ) - internal - pure - returns (bytes32 result) - { - bytes32 schemaHash = EIP712_ZEROEX_TRANSACTION_SCHEMA_HASH; - bytes32 dataHash = keccak256(data); - - // Assembly for more efficiently computing: - // keccak256(abi.encodePacked( - // EIP712_ZEROEX_TRANSACTION_SCHEMA_HASH, - // salt, - // bytes32(signerAddress), - // keccak256(data) - // )); - - assembly { - // Load free memory pointer - let memPtr := mload(64) - - mstore(memPtr, schemaHash) // hash of schema - mstore(add(memPtr, 32), salt) // salt - mstore(add(memPtr, 64), and(signerAddress, 0xffffffffffffffffffffffffffffffffffffffff)) // signerAddress - mstore(add(memPtr, 96), dataHash) // hash of data - - // Compute hash - result := keccak256(memPtr, 128) - } - return result; - } - - /// @dev The current function will be called in the context of this address (either 0x transaction signer or `msg.sender`). - /// If calling a fill function, this address will represent the taker. - /// If calling a cancel function, this address will represent the maker. - /// @return Signer of 0x transaction if entry point is `executeTransaction`. - /// `msg.sender` if entry point is any other function. - function getCurrentContextAddress() - internal - view - returns (address) - { - address currentContextAddress_ = currentContextAddress; - address contextAddress = currentContextAddress_ == address(0) ? msg.sender : currentContextAddress_; - return contextAddress; - } -} diff --git a/contracts/core/contracts/protocol/Exchange/MixinWrapperFunctions.sol b/contracts/core/contracts/protocol/Exchange/MixinWrapperFunctions.sol deleted file mode 100644 index 2d43432ff..000000000 --- a/contracts/core/contracts/protocol/Exchange/MixinWrapperFunctions.sol +++ /dev/null @@ -1,426 +0,0 @@ -/* - - 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 "@0x/contracts-utils/contracts/utils/ReentrancyGuard/ReentrancyGuard.sol"; -import "@0x/contracts-libs/contracts/libs/LibMath.sol"; -import "@0x/contracts-libs/contracts/libs/LibOrder.sol"; -import "@0x/contracts-libs/contracts/libs/LibFillResults.sol"; -import "@0x/contracts-libs/contracts/libs/LibAbiEncoder.sol"; -import "./mixins/MExchangeCore.sol"; -import "./mixins/MWrapperFunctions.sol"; - - -contract MixinWrapperFunctions is - ReentrancyGuard, - LibMath, - LibFillResults, - LibAbiEncoder, - MExchangeCore, - MWrapperFunctions -{ - /// @dev Fills the input order. Reverts if exact takerAssetFillAmount not filled. - /// @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. - function fillOrKillOrder( - LibOrder.Order memory order, - uint256 takerAssetFillAmount, - bytes memory signature - ) - public - nonReentrant - returns (FillResults memory fillResults) - { - fillResults = fillOrKillOrderInternal( - order, - takerAssetFillAmount, - signature - ); - return fillResults; - } - - /// @dev Fills the input order. - /// Returns false if the transaction would otherwise revert. - /// @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 Amounts filled and fees paid by maker and taker. - function fillOrderNoThrow( - LibOrder.Order memory order, - uint256 takerAssetFillAmount, - bytes memory signature - ) - public - returns (FillResults memory fillResults) - { - // ABI encode calldata for `fillOrder` - bytes memory fillOrderCalldata = abiEncodeFillOrder( - order, - takerAssetFillAmount, - signature - ); - - // Delegate to `fillOrder` and handle any exceptions gracefully - assembly { - let success := delegatecall( - gas, // forward all gas - address, // call address of this contract - add(fillOrderCalldata, 32), // pointer to start of input (skip array length in first 32 bytes) - mload(fillOrderCalldata), // length of input - fillOrderCalldata, // write output over input - 128 // output size is 128 bytes - ) - if success { - mstore(fillResults, mload(fillOrderCalldata)) - mstore(add(fillResults, 32), mload(add(fillOrderCalldata, 32))) - mstore(add(fillResults, 64), mload(add(fillOrderCalldata, 64))) - mstore(add(fillResults, 96), mload(add(fillOrderCalldata, 96))) - } - } - // fillResults values will be 0 by default if call was unsuccessful - return fillResults; - } - - /// @dev Synchronously executes multiple calls of fillOrder. - /// @param orders Array of order specifications. - /// @param takerAssetFillAmounts Array of desired amounts of takerAsset to sell in orders. - /// @param signatures Proofs that orders have been created by makers. - /// @return Amounts filled and fees paid by makers and taker. - /// NOTE: makerAssetFilledAmount and takerAssetFilledAmount may include amounts filled of different assets. - function batchFillOrders( - LibOrder.Order[] memory orders, - uint256[] memory takerAssetFillAmounts, - bytes[] memory signatures - ) - public - nonReentrant - returns (FillResults memory totalFillResults) - { - uint256 ordersLength = orders.length; - for (uint256 i = 0; i != ordersLength; i++) { - FillResults memory singleFillResults = fillOrderInternal( - orders[i], - takerAssetFillAmounts[i], - signatures[i] - ); - addFillResults(totalFillResults, singleFillResults); - } - return totalFillResults; - } - - /// @dev Synchronously executes multiple calls of fillOrKill. - /// @param orders Array of order specifications. - /// @param takerAssetFillAmounts Array of desired amounts of takerAsset to sell in orders. - /// @param signatures Proofs that orders have been created by makers. - /// @return Amounts filled and fees paid by makers and taker. - /// NOTE: makerAssetFilledAmount and takerAssetFilledAmount may include amounts filled of different assets. - function batchFillOrKillOrders( - LibOrder.Order[] memory orders, - uint256[] memory takerAssetFillAmounts, - bytes[] memory signatures - ) - public - nonReentrant - returns (FillResults memory totalFillResults) - { - uint256 ordersLength = orders.length; - for (uint256 i = 0; i != ordersLength; i++) { - FillResults memory singleFillResults = fillOrKillOrderInternal( - orders[i], - takerAssetFillAmounts[i], - signatures[i] - ); - addFillResults(totalFillResults, singleFillResults); - } - return totalFillResults; - } - - /// @dev Fills an order with specified parameters and ECDSA signature. - /// Returns false if the transaction would otherwise revert. - /// @param orders Array of order specifications. - /// @param takerAssetFillAmounts Array of desired amounts of takerAsset to sell in orders. - /// @param signatures Proofs that orders have been created by makers. - /// @return Amounts filled and fees paid by makers and taker. - /// NOTE: makerAssetFilledAmount and takerAssetFilledAmount may include amounts filled of different assets. - function batchFillOrdersNoThrow( - LibOrder.Order[] memory orders, - uint256[] memory takerAssetFillAmounts, - bytes[] memory signatures - ) - public - returns (FillResults memory totalFillResults) - { - uint256 ordersLength = orders.length; - for (uint256 i = 0; i != ordersLength; i++) { - FillResults memory singleFillResults = fillOrderNoThrow( - orders[i], - takerAssetFillAmounts[i], - signatures[i] - ); - addFillResults(totalFillResults, singleFillResults); - } - return totalFillResults; - } - - /// @dev Synchronously executes multiple calls of fillOrder until total amount of takerAsset is sold by taker. - /// @param orders Array of order specifications. - /// @param takerAssetFillAmount Desired amount of takerAsset to sell. - /// @param signatures Proofs that orders have been created by makers. - /// @return Amounts filled and fees paid by makers and taker. - function marketSellOrders( - LibOrder.Order[] memory orders, - uint256 takerAssetFillAmount, - bytes[] memory signatures - ) - public - nonReentrant - returns (FillResults memory totalFillResults) - { - bytes memory takerAssetData = orders[0].takerAssetData; - - uint256 ordersLength = orders.length; - for (uint256 i = 0; i != ordersLength; i++) { - - // We assume that asset being sold by taker is the same for each order. - // Rather than passing this in as calldata, we use the takerAssetData from the first order in all later orders. - orders[i].takerAssetData = takerAssetData; - - // Calculate the remaining amount of takerAsset to sell - uint256 remainingTakerAssetFillAmount = safeSub(takerAssetFillAmount, totalFillResults.takerAssetFilledAmount); - - // Attempt to sell the remaining amount of takerAsset - FillResults memory singleFillResults = fillOrderInternal( - orders[i], - remainingTakerAssetFillAmount, - signatures[i] - ); - - // Update amounts filled and fees paid by maker and taker - addFillResults(totalFillResults, singleFillResults); - - // Stop execution if the entire amount of takerAsset has been sold - if (totalFillResults.takerAssetFilledAmount >= takerAssetFillAmount) { - break; - } - } - return totalFillResults; - } - - /// @dev Synchronously executes multiple calls of fillOrder until total amount of takerAsset is sold by taker. - /// Returns false if the transaction would otherwise revert. - /// @param orders Array of order specifications. - /// @param takerAssetFillAmount Desired amount of takerAsset to sell. - /// @param signatures Proofs that orders have been signed by makers. - /// @return Amounts filled and fees paid by makers and taker. - function marketSellOrdersNoThrow( - LibOrder.Order[] memory orders, - uint256 takerAssetFillAmount, - bytes[] memory signatures - ) - public - returns (FillResults memory totalFillResults) - { - bytes memory takerAssetData = orders[0].takerAssetData; - - uint256 ordersLength = orders.length; - for (uint256 i = 0; i != ordersLength; i++) { - - // We assume that asset being sold by taker is the same for each order. - // Rather than passing this in as calldata, we use the takerAssetData from the first order in all later orders. - orders[i].takerAssetData = takerAssetData; - - // Calculate the remaining amount of takerAsset to sell - uint256 remainingTakerAssetFillAmount = safeSub(takerAssetFillAmount, totalFillResults.takerAssetFilledAmount); - - // Attempt to sell the remaining amount of takerAsset - FillResults memory singleFillResults = fillOrderNoThrow( - orders[i], - remainingTakerAssetFillAmount, - signatures[i] - ); - - // Update amounts filled and fees paid by maker and taker - addFillResults(totalFillResults, singleFillResults); - - // Stop execution if the entire amount of takerAsset has been sold - if (totalFillResults.takerAssetFilledAmount >= takerAssetFillAmount) { - break; - } - } - return totalFillResults; - } - - /// @dev Synchronously executes multiple calls of fillOrder until total amount of makerAsset is bought by taker. - /// @param orders Array of order specifications. - /// @param makerAssetFillAmount Desired amount of makerAsset to buy. - /// @param signatures Proofs that orders have been signed by makers. - /// @return Amounts filled and fees paid by makers and taker. - function marketBuyOrders( - LibOrder.Order[] memory orders, - uint256 makerAssetFillAmount, - bytes[] memory signatures - ) - public - nonReentrant - returns (FillResults memory totalFillResults) - { - bytes memory makerAssetData = orders[0].makerAssetData; - - uint256 ordersLength = orders.length; - for (uint256 i = 0; i != ordersLength; i++) { - - // We assume that asset being bought by taker is the same for each order. - // Rather than passing this in as calldata, we copy the makerAssetData from the first order onto all later orders. - orders[i].makerAssetData = makerAssetData; - - // Calculate the remaining amount of makerAsset to buy - uint256 remainingMakerAssetFillAmount = safeSub(makerAssetFillAmount, totalFillResults.makerAssetFilledAmount); - - // Convert the remaining amount of makerAsset to buy into remaining amount - // of takerAsset to sell, assuming entire amount can be sold in the current order - uint256 remainingTakerAssetFillAmount = getPartialAmountFloor( - orders[i].takerAssetAmount, - orders[i].makerAssetAmount, - remainingMakerAssetFillAmount - ); - - // Attempt to sell the remaining amount of takerAsset - FillResults memory singleFillResults = fillOrderInternal( - orders[i], - remainingTakerAssetFillAmount, - signatures[i] - ); - - // Update amounts filled and fees paid by maker and taker - addFillResults(totalFillResults, singleFillResults); - - // Stop execution if the entire amount of makerAsset has been bought - if (totalFillResults.makerAssetFilledAmount >= makerAssetFillAmount) { - break; - } - } - return totalFillResults; - } - - /// @dev Synchronously executes multiple fill orders in a single transaction until total amount is bought by taker. - /// Returns false if the transaction would otherwise revert. - /// @param orders Array of order specifications. - /// @param makerAssetFillAmount Desired amount of makerAsset to buy. - /// @param signatures Proofs that orders have been signed by makers. - /// @return Amounts filled and fees paid by makers and taker. - function marketBuyOrdersNoThrow( - LibOrder.Order[] memory orders, - uint256 makerAssetFillAmount, - bytes[] memory signatures - ) - public - returns (FillResults memory totalFillResults) - { - bytes memory makerAssetData = orders[0].makerAssetData; - - uint256 ordersLength = orders.length; - for (uint256 i = 0; i != ordersLength; i++) { - - // We assume that asset being bought by taker is the same for each order. - // Rather than passing this in as calldata, we copy the makerAssetData from the first order onto all later orders. - orders[i].makerAssetData = makerAssetData; - - // Calculate the remaining amount of makerAsset to buy - uint256 remainingMakerAssetFillAmount = safeSub(makerAssetFillAmount, totalFillResults.makerAssetFilledAmount); - - // Convert the remaining amount of makerAsset to buy into remaining amount - // of takerAsset to sell, assuming entire amount can be sold in the current order - uint256 remainingTakerAssetFillAmount = getPartialAmountFloor( - orders[i].takerAssetAmount, - orders[i].makerAssetAmount, - remainingMakerAssetFillAmount - ); - - // Attempt to sell the remaining amount of takerAsset - FillResults memory singleFillResults = fillOrderNoThrow( - orders[i], - remainingTakerAssetFillAmount, - signatures[i] - ); - - // Update amounts filled and fees paid by maker and taker - addFillResults(totalFillResults, singleFillResults); - - // Stop execution if the entire amount of makerAsset has been bought - if (totalFillResults.makerAssetFilledAmount >= makerAssetFillAmount) { - break; - } - } - return totalFillResults; - } - - /// @dev Synchronously cancels multiple orders in a single transaction. - /// @param orders Array of order specifications. - function batchCancelOrders(LibOrder.Order[] memory orders) - public - nonReentrant - { - uint256 ordersLength = orders.length; - for (uint256 i = 0; i != ordersLength; i++) { - cancelOrderInternal(orders[i]); - } - } - - /// @dev Fetches information for all passed in orders. - /// @param orders Array of order specifications. - /// @return Array of OrderInfo instances that correspond to each order. - function getOrdersInfo(LibOrder.Order[] memory orders) - public - view - returns (LibOrder.OrderInfo[] memory) - { - uint256 ordersLength = orders.length; - LibOrder.OrderInfo[] memory ordersInfo = new LibOrder.OrderInfo[](ordersLength); - for (uint256 i = 0; i != ordersLength; i++) { - ordersInfo[i] = getOrderInfo(orders[i]); - } - return ordersInfo; - } - - /// @dev Fills the input order. Reverts if exact takerAssetFillAmount not filled. - /// @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. - function fillOrKillOrderInternal( - LibOrder.Order memory order, - uint256 takerAssetFillAmount, - bytes memory signature - ) - internal - returns (FillResults memory fillResults) - { - fillResults = fillOrderInternal( - order, - takerAssetFillAmount, - signature - ); - require( - fillResults.takerAssetFilledAmount == takerAssetFillAmount, - "COMPLETE_FILL_FAILED" - ); - return fillResults; - } -} diff --git a/contracts/core/contracts/protocol/Exchange/mixins/MAssetProxyDispatcher.sol b/contracts/core/contracts/protocol/Exchange/mixins/MAssetProxyDispatcher.sol deleted file mode 100644 index fe3d03326..000000000 --- a/contracts/core/contracts/protocol/Exchange/mixins/MAssetProxyDispatcher.sol +++ /dev/null @@ -1,45 +0,0 @@ -/* - - 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-interfaces/contracts/protocol/Exchange/IAssetProxyDispatcher.sol"; - - -contract MAssetProxyDispatcher is - IAssetProxyDispatcher -{ - // Logs registration of new asset proxy - event AssetProxyRegistered( - bytes4 id, // Id of new registered AssetProxy. - address assetProxy // Address of new registered AssetProxy. - ); - - /// @dev Forwards arguments to assetProxy and calls `transferFrom`. Either succeeds or throws. - /// @param assetData Byte array encoded for the asset. - /// @param from Address to transfer token from. - /// @param to Address to transfer token to. - /// @param amount Amount of token to transfer. - function dispatchTransferFrom( - bytes memory assetData, - address from, - address to, - uint256 amount - ) - internal; -} diff --git a/contracts/core/contracts/protocol/Exchange/mixins/MExchangeCore.sol b/contracts/core/contracts/protocol/Exchange/mixins/MExchangeCore.sol deleted file mode 100644 index 215284900..000000000 --- a/contracts/core/contracts/protocol/Exchange/mixins/MExchangeCore.sol +++ /dev/null @@ -1,157 +0,0 @@ -/* - - 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 "@0x/contracts-libs/contracts/libs/LibOrder.sol"; -import "@0x/contracts-libs/contracts/libs/LibFillResults.sol"; -import "@0x/contracts-interfaces/contracts/protocol/Exchange/IExchangeCore.sol"; - - -contract MExchangeCore is - IExchangeCore -{ - // Fill event is emitted whenever an order is filled. - event Fill( - address indexed makerAddress, // Address that created the order. - address indexed feeRecipientAddress, // Address that received fees. - address takerAddress, // Address that filled the order. - address senderAddress, // Address that called the Exchange contract (msg.sender). - uint256 makerAssetFilledAmount, // Amount of makerAsset sold by maker and bought by taker. - uint256 takerAssetFilledAmount, // Amount of takerAsset sold by taker and bought by maker. - uint256 makerFeePaid, // Amount of ZRX paid to feeRecipient by maker. - uint256 takerFeePaid, // Amount of ZRX paid to feeRecipient by taker. - bytes32 indexed orderHash, // EIP712 hash of order (see LibOrder.getOrderHash). - bytes makerAssetData, // Encoded data specific to makerAsset. - bytes takerAssetData // Encoded data specific to takerAsset. - ); - - // Cancel event is emitted whenever an individual order is cancelled. - event Cancel( - address indexed makerAddress, // Address that created the order. - address indexed feeRecipientAddress, // Address that would have recieved fees if order was filled. - address senderAddress, // Address that called the Exchange contract (msg.sender). - bytes32 indexed orderHash, // EIP712 hash of order (see LibOrder.getOrderHash). - bytes makerAssetData, // Encoded data specific to makerAsset. - bytes takerAssetData // Encoded data specific to takerAsset. - ); - - // CancelUpTo event is emitted whenever `cancelOrdersUpTo` is executed succesfully. - event CancelUpTo( - address indexed makerAddress, // Orders cancelled must have been created by this address. - address indexed senderAddress, // Orders cancelled must have a `senderAddress` equal to this address. - uint256 orderEpoch // Orders with specified makerAddress and senderAddress with a salt less than this value are considered cancelled. - ); - - /// @dev Fills the input order. - /// @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 Amounts filled and fees paid by maker and taker. - function fillOrderInternal( - LibOrder.Order memory order, - uint256 takerAssetFillAmount, - bytes memory signature - ) - internal - returns (LibFillResults.FillResults memory fillResults); - - /// @dev After calling, the order can not be filled anymore. - /// @param order Order struct containing order specifications. - function cancelOrderInternal(LibOrder.Order memory order) - internal; - - /// @dev Updates state with results of a fill order. - /// @param order that was filled. - /// @param takerAddress Address of taker who filled the order. - /// @param orderTakerAssetFilledAmount Amount of order already filled. - /// @return fillResults Amounts filled and fees paid by maker and taker. - function updateFilledState( - LibOrder.Order memory order, - address takerAddress, - bytes32 orderHash, - uint256 orderTakerAssetFilledAmount, - LibFillResults.FillResults memory fillResults - ) - internal; - - /// @dev Updates state with results of cancelling an order. - /// State is only updated if the order is currently fillable. - /// Otherwise, updating state would have no effect. - /// @param order that was cancelled. - /// @param orderHash Hash of order that was cancelled. - function updateCancelledState( - LibOrder.Order memory order, - bytes32 orderHash - ) - internal; - - /// @dev Validates context for fillOrder. Succeeds or throws. - /// @param order to be filled. - /// @param orderInfo OrderStatus, orderHash, and amount already filled of order. - /// @param takerAddress Address of order taker. - /// @param signature Proof that the orders was created by its maker. - function assertFillableOrder( - LibOrder.Order memory order, - LibOrder.OrderInfo memory orderInfo, - address takerAddress, - bytes memory signature - ) - internal - view; - - /// @dev Validates context for fillOrder. Succeeds or throws. - /// @param order to be filled. - /// @param orderInfo Status, orderHash, and amount already filled of order. - /// @param takerAssetFillAmount Desired amount of order to fill by taker. - /// @param takerAssetFilledAmount Amount of takerAsset that will be filled. - /// @param makerAssetFilledAmount Amount of makerAsset that will be transfered. - function assertValidFill( - LibOrder.Order memory order, - LibOrder.OrderInfo memory orderInfo, - uint256 takerAssetFillAmount, - uint256 takerAssetFilledAmount, - uint256 makerAssetFilledAmount - ) - internal - view; - - /// @dev Validates context for cancelOrder. Succeeds or throws. - /// @param order to be cancelled. - /// @param orderInfo OrderStatus, orderHash, and amount already filled of order. - function assertValidCancel( - LibOrder.Order memory order, - LibOrder.OrderInfo memory orderInfo - ) - internal - view; - - /// @dev Calculates amounts filled and fees paid by maker and taker. - /// @param order to be filled. - /// @param takerAssetFilledAmount Amount of takerAsset that will be filled. - /// @return fillResults Amounts filled and fees paid by maker and taker. - function calculateFillResults( - LibOrder.Order memory order, - uint256 takerAssetFilledAmount - ) - internal - pure - returns (LibFillResults.FillResults memory fillResults); - -} diff --git a/contracts/core/contracts/protocol/Exchange/mixins/MMatchOrders.sol b/contracts/core/contracts/protocol/Exchange/mixins/MMatchOrders.sol deleted file mode 100644 index 1eb4be329..000000000 --- a/contracts/core/contracts/protocol/Exchange/mixins/MMatchOrders.sol +++ /dev/null @@ -1,58 +0,0 @@ -/* - - 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 "@0x/contracts-libs/contracts/libs/LibOrder.sol"; -import "@0x/contracts-libs/contracts/libs/LibFillResults.sol"; -import "@0x/contracts-interfaces/contracts/protocol/Exchange/IMatchOrders.sol"; - - -contract MMatchOrders is - IMatchOrders -{ - /// @dev Validates context for matchOrders. Succeeds or throws. - /// @param leftOrder First order to match. - /// @param rightOrder Second order to match. - function assertValidMatch( - LibOrder.Order memory leftOrder, - LibOrder.Order memory rightOrder - ) - internal - pure; - - /// @dev Calculates fill amounts for the matched orders. - /// Each order is filled at their respective price point. However, the calculations are - /// carried out as though the orders are both being filled at the right order's price point. - /// The profit made by the leftOrder order goes to the taker (who matched the two orders). - /// @param leftOrder First order to match. - /// @param rightOrder Second order to match. - /// @param leftOrderTakerAssetFilledAmount Amount of left order already filled. - /// @param rightOrderTakerAssetFilledAmount Amount of right order already filled. - /// @param matchedFillResults Amounts to fill and fees to pay by maker and taker of matched orders. - function calculateMatchedFillResults( - LibOrder.Order memory leftOrder, - LibOrder.Order memory rightOrder, - uint256 leftOrderTakerAssetFilledAmount, - uint256 rightOrderTakerAssetFilledAmount - ) - internal - pure - returns (LibFillResults.MatchedFillResults memory matchedFillResults); - -} diff --git a/contracts/core/contracts/protocol/Exchange/mixins/MSignatureValidator.sol b/contracts/core/contracts/protocol/Exchange/mixins/MSignatureValidator.sol deleted file mode 100644 index a933976d1..000000000 --- a/contracts/core/contracts/protocol/Exchange/mixins/MSignatureValidator.sol +++ /dev/null @@ -1,75 +0,0 @@ -/* - - 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-interfaces/contracts/protocol/Exchange/ISignatureValidator.sol"; - - -contract MSignatureValidator is - ISignatureValidator -{ - event SignatureValidatorApproval( - address indexed signerAddress, // Address that approves or disapproves a contract to verify signatures. - address indexed validatorAddress, // Address of signature validator contract. - bool approved // Approval or disapproval of validator contract. - ); - - // Allowed signature types. - enum SignatureType { - Illegal, // 0x00, default value - Invalid, // 0x01 - EIP712, // 0x02 - EthSign, // 0x03 - Wallet, // 0x04 - Validator, // 0x05 - PreSigned, // 0x06 - NSignatureTypes // 0x07, number of signature types. Always leave at end. - } - - /// @dev Verifies signature using logic defined by Wallet contract. - /// @param hash Any 32 byte hash. - /// @param walletAddress Address that should have signed the given hash - /// and defines its own signature verification method. - /// @param signature Proof that the hash has been signed by signer. - /// @return True if the address recovered from the provided signature matches the input signer address. - function isValidWalletSignature( - bytes32 hash, - address walletAddress, - bytes signature - ) - internal - view - returns (bool isValid); - - /// @dev Verifies signature using logic defined by Validator contract. - /// @param validatorAddress Address of validator contract. - /// @param hash Any 32 byte hash. - /// @param signerAddress Address that should have signed the given hash. - /// @param signature Proof that the hash has been signed by signer. - /// @return True if the address recovered from the provided signature matches the input signer address. - function isValidValidatorSignature( - address validatorAddress, - bytes32 hash, - address signerAddress, - bytes signature - ) - internal - view - returns (bool isValid); -} diff --git a/contracts/core/contracts/protocol/Exchange/mixins/MTransactions.sol b/contracts/core/contracts/protocol/Exchange/mixins/MTransactions.sol deleted file mode 100644 index a6b0fdc85..000000000 --- a/contracts/core/contracts/protocol/Exchange/mixins/MTransactions.sol +++ /dev/null @@ -1,58 +0,0 @@ -/* - - 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-interfaces/contracts/protocol/Exchange/ITransactions.sol"; - - -contract MTransactions is - ITransactions -{ - // Hash for the EIP712 ZeroEx Transaction Schema - bytes32 constant internal EIP712_ZEROEX_TRANSACTION_SCHEMA_HASH = keccak256(abi.encodePacked( - "ZeroExTransaction(", - "uint256 salt,", - "address signerAddress,", - "bytes data", - ")" - )); - - /// @dev Calculates EIP712 hash of the Transaction. - /// @param salt Arbitrary number to ensure uniqueness of transaction hash. - /// @param signerAddress Address of transaction signer. - /// @param data AbiV2 encoded calldata. - /// @return EIP712 hash of the Transaction. - function hashZeroExTransaction( - uint256 salt, - address signerAddress, - bytes memory data - ) - internal - pure - returns (bytes32 result); - - /// @dev The current function will be called in the context of this address (either 0x transaction signer or `msg.sender`). - /// If calling a fill function, this address will represent the taker. - /// If calling a cancel function, this address will represent the maker. - /// @return Signer of 0x transaction if entry point is `executeTransaction`. - /// `msg.sender` if entry point is any other function. - function getCurrentContextAddress() - internal - view - returns (address); -} diff --git a/contracts/core/contracts/protocol/Exchange/mixins/MWrapperFunctions.sol b/contracts/core/contracts/protocol/Exchange/mixins/MWrapperFunctions.sol deleted file mode 100644 index 101e7cb82..000000000 --- a/contracts/core/contracts/protocol/Exchange/mixins/MWrapperFunctions.sol +++ /dev/null @@ -1,41 +0,0 @@ -/* - - 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 "@0x/contracts-libs/contracts/libs/LibOrder.sol"; -import "@0x/contracts-libs/contracts/libs/LibFillResults.sol"; -import "@0x/contracts-interfaces/contracts/protocol/Exchange/IWrapperFunctions.sol"; - - -contract MWrapperFunctions is - IWrapperFunctions -{ - /// @dev Fills the input order. Reverts if exact takerAssetFillAmount not filled. - /// @param order LibOrder.Order struct containing order specifications. - /// @param takerAssetFillAmount Desired amount of takerAsset to sell. - /// @param signature Proof that order has been created by maker. - function fillOrKillOrderInternal( - LibOrder.Order memory order, - uint256 takerAssetFillAmount, - bytes memory signature - ) - internal - returns (LibFillResults.FillResults memory fillResults); -} diff --git a/contracts/core/contracts/test/TestAssetProxyDispatcher/TestAssetProxyDispatcher.sol b/contracts/core/contracts/test/TestAssetProxyDispatcher/TestAssetProxyDispatcher.sol deleted file mode 100644 index ad71fc9a1..000000000 --- a/contracts/core/contracts/test/TestAssetProxyDispatcher/TestAssetProxyDispatcher.sol +++ /dev/null @@ -1,37 +0,0 @@ -/* - - 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 "../../protocol/Exchange/MixinAssetProxyDispatcher.sol"; - - -contract TestAssetProxyDispatcher is - MixinAssetProxyDispatcher -{ - function publicDispatchTransferFrom( - bytes memory assetData, - address from, - address to, - uint256 amount - ) - public - { - dispatchTransferFrom(assetData, from, to, amount); - } -} diff --git a/contracts/core/contracts/test/TestAssetProxyOwner/TestAssetProxyOwner.sol b/contracts/core/contracts/test/TestAssetProxyOwner/TestAssetProxyOwner.sol deleted file mode 100644 index 52c66cb56..000000000 --- a/contracts/core/contracts/test/TestAssetProxyOwner/TestAssetProxyOwner.sol +++ /dev/null @@ -1,58 +0,0 @@ -/* - - 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 "../../protocol/AssetProxyOwner/AssetProxyOwner.sol"; - - -// solhint-disable no-empty-blocks -contract TestAssetProxyOwner is - AssetProxyOwner -{ - constructor ( - address[] memory _owners, - address[] memory _assetProxyContracts, - uint256 _required, - uint256 _secondsTimeLocked - ) - public - AssetProxyOwner(_owners, _assetProxyContracts, _required, _secondsTimeLocked) - {} - - function testValidRemoveAuthorizedAddressAtIndexTx(uint256 id) - public - view - validRemoveAuthorizedAddressAtIndexTx(id) - returns (bool) - { - // Do nothing. We expect reverts through the modifier - return true; - } - - /// @dev Compares first 4 bytes of byte array to `removeAuthorizedAddressAtIndex` function selector. - /// @param data Transaction data. - /// @return Successful if data is a call to `removeAuthorizedAddressAtIndex`. - function isFunctionRemoveAuthorizedAddressAtIndex(bytes memory data) - public - pure - returns (bool) - { - return data.readBytes4(0) == REMOVE_AUTHORIZED_ADDRESS_AT_INDEX_SELECTOR; - } -} diff --git a/contracts/core/contracts/test/TestExchangeInternals/TestExchangeInternals.sol b/contracts/core/contracts/test/TestExchangeInternals/TestExchangeInternals.sol deleted file mode 100644 index 27187f8f8..000000000 --- a/contracts/core/contracts/test/TestExchangeInternals/TestExchangeInternals.sol +++ /dev/null @@ -1,191 +0,0 @@ -/* - - 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 "../../protocol/Exchange/Exchange.sol"; - - -// solhint-disable no-empty-blocks -contract TestExchangeInternals is - Exchange -{ - constructor () - public - Exchange("") - {} - - /// @dev Adds properties of both FillResults instances. - /// Modifies the first FillResults instance specified. - /// Note that this function has been modified from the original - // internal version to return the FillResults. - /// @param totalFillResults Fill results instance that will be added onto. - /// @param singleFillResults Fill results instance that will be added to totalFillResults. - /// @return newTotalFillResults The result of adding singleFillResults to totalFilResults. - function publicAddFillResults(FillResults memory totalFillResults, FillResults memory singleFillResults) - public - pure - returns (FillResults memory) - { - addFillResults(totalFillResults, singleFillResults); - return totalFillResults; - } - - /// @dev Calculates amounts filled and fees paid by maker and taker. - /// @param order to be filled. - /// @param takerAssetFilledAmount Amount of takerAsset that will be filled. - /// @return fillResults Amounts filled and fees paid by maker and taker. - function publicCalculateFillResults( - Order memory order, - uint256 takerAssetFilledAmount - ) - public - pure - returns (FillResults memory fillResults) - { - return calculateFillResults(order, takerAssetFilledAmount); - } - - /// @dev Calculates partial value given a numerator and denominator. - /// 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. - function publicSafeGetPartialAmountFloor( - uint256 numerator, - uint256 denominator, - uint256 target - ) - public - pure - returns (uint256 partialAmount) - { - return safeGetPartialAmountFloor(numerator, denominator, target); - } - - /// @dev Calculates partial value given a numerator and denominator. - /// 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. - function publicSafeGetPartialAmountCeil( - uint256 numerator, - uint256 denominator, - uint256 target - ) - public - pure - returns (uint256 partialAmount) - { - return safeGetPartialAmountCeil(numerator, denominator, target); - } - - /// @dev Calculates partial value given a numerator and denominator. - /// @param numerator Numerator. - /// @param denominator Denominator. - /// @param target Value to calculate partial of. - /// @return Partial value of target. - function publicGetPartialAmountFloor( - uint256 numerator, - uint256 denominator, - uint256 target - ) - public - pure - returns (uint256 partialAmount) - { - return getPartialAmountFloor(numerator, denominator, target); - } - - /// @dev Calculates partial value given a numerator and denominator. - /// @param numerator Numerator. - /// @param denominator Denominator. - /// @param target Value to calculate partial of. - /// @return Partial value of target. - function publicGetPartialAmountCeil( - uint256 numerator, - uint256 denominator, - uint256 target - ) - public - pure - returns (uint256 partialAmount) - { - return getPartialAmountCeil(numerator, denominator, target); - } - - /// @dev Checks if rounding error >= 0.1%. - /// @param numerator Numerator. - /// @param denominator Denominator. - /// @param target Value to multiply with numerator/denominator. - /// @return Rounding error is present. - function publicIsRoundingErrorFloor( - uint256 numerator, - uint256 denominator, - uint256 target - ) - public - pure - returns (bool isError) - { - return isRoundingErrorFloor(numerator, denominator, target); - } - - /// @dev Checks if rounding error >= 0.1%. - /// @param numerator Numerator. - /// @param denominator Denominator. - /// @param target Value to multiply with numerator/denominator. - /// @return Rounding error is present. - function publicIsRoundingErrorCeil( - uint256 numerator, - uint256 denominator, - uint256 target - ) - public - pure - returns (bool isError) - { - return isRoundingErrorCeil(numerator, denominator, target); - } - - /// @dev Updates state with results of a fill order. - /// @param order that was filled. - /// @param takerAddress Address of taker who filled the order. - /// @param orderTakerAssetFilledAmount Amount of order already filled. - /// @return fillResults Amounts filled and fees paid by maker and taker. - function publicUpdateFilledState( - Order memory order, - address takerAddress, - bytes32 orderHash, - uint256 orderTakerAssetFilledAmount, - FillResults memory fillResults - ) - public - { - updateFilledState( - order, - takerAddress, - orderHash, - orderTakerAssetFilledAmount, - fillResults - ); - } -} diff --git a/contracts/core/contracts/test/TestSignatureValidator/TestSignatureValidator.sol b/contracts/core/contracts/test/TestSignatureValidator/TestSignatureValidator.sol deleted file mode 100644 index ea3e2de59..000000000 --- a/contracts/core/contracts/test/TestSignatureValidator/TestSignatureValidator.sol +++ /dev/null @@ -1,45 +0,0 @@ -/* - - 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 "../../protocol/Exchange/MixinSignatureValidator.sol"; -import "../../protocol/Exchange/MixinTransactions.sol"; - - -contract TestSignatureValidator is - MixinSignatureValidator, - MixinTransactions -{ - function publicIsValidSignature( - bytes32 hash, - address signer, - bytes memory signature - ) - public - view - returns (bool isValid) - { - isValid = isValidSignature( - hash, - signer, - signature - ); - return isValid; - } -} diff --git a/contracts/core/contracts/test/TestStaticCallReceiver/TestStaticCallReceiver.sol b/contracts/core/contracts/test/TestStaticCallReceiver/TestStaticCallReceiver.sol deleted file mode 100644 index d08da7303..000000000 --- a/contracts/core/contracts/test/TestStaticCallReceiver/TestStaticCallReceiver.sol +++ /dev/null @@ -1,81 +0,0 @@ -/* - - 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-tokens/contracts/tokens/ERC20Token/IERC20Token.sol"; - - -// solhint-disable no-unused-vars -contract TestStaticCallReceiver { - - uint256 internal state = 1; - - /// @dev Updates state and returns true. Intended to be used with `Validator` signature type. - /// @param hash Message hash that is signed. - /// @param signerAddress Address that should have signed the given hash. - /// @param signature Proof of signing. - /// @return Validity of order signature. - function isValidSignature( - bytes32 hash, - address signerAddress, - bytes signature - ) - external - returns (bool isValid) - { - updateState(); - return true; - } - - /// @dev Updates state and returns true. Intended to be used with `Wallet` signature type. - /// @param hash Message hash that is signed. - /// @param signature Proof of signing. - /// @return Validity of order signature. - function isValidSignature( - bytes32 hash, - bytes signature - ) - external - returns (bool isValid) - { - updateState(); - return true; - } - - /// @dev Approves an ERC20 token to spend tokens from this address. - /// @param token Address of ERC20 token. - /// @param spender Address that will spend tokens. - /// @param value Amount of tokens spender is approved to spend. - function approveERC20( - address token, - address spender, - uint256 value - ) - external - { - IERC20Token(token).approve(spender, value); - } - - /// @dev Increments state variable. - function updateState() - internal - { - state++; - } -} diff --git a/contracts/core/package.json b/contracts/core/package.json deleted file mode 100644 index b7077f4b4..000000000 --- a/contracts/core/package.json +++ /dev/null @@ -1,96 +0,0 @@ -{ - "private": true, - "name": "@0x/contracts-core", - "version": "2.1.56", - "engines": { - "node": ">=6.12" - }, - "description": "Smart contract components 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/@(AssetProxyOwner|ERC20Proxy|ERC721Proxy|Exchange|MixinAuthorizable|MultiAssetProxy|TestSignatureValidator|TestAssetProxyOwner|TestAssetProxyDispatcher|TestExchangeInternals|TestStaticCallReceiver).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/core/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/contracts-tokens": "^1.0.0", - "@0x/contracts-examples": "^1.0.0", - "@0x/contracts-libs": "^1.0.0", - "@0x/contracts-interfaces": "^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/core/src/artifacts/index.ts b/contracts/core/src/artifacts/index.ts deleted file mode 100644 index c5d12f10b..000000000 --- a/contracts/core/src/artifacts/index.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { ContractArtifact } from 'ethereum-types'; - -import * as AssetProxyOwner from '../../generated-artifacts/AssetProxyOwner.json'; -import * as ERC20Proxy from '../../generated-artifacts/ERC20Proxy.json'; -import * as ERC721Proxy from '../../generated-artifacts/ERC721Proxy.json'; -import * as Exchange from '../../generated-artifacts/Exchange.json'; -import * as MixinAuthorizable from '../../generated-artifacts/MixinAuthorizable.json'; -import * as MultiAssetProxy from '../../generated-artifacts/MultiAssetProxy.json'; -import * as TestAssetProxyDispatcher from '../../generated-artifacts/TestAssetProxyDispatcher.json'; -import * as TestAssetProxyOwner from '../../generated-artifacts/TestAssetProxyOwner.json'; -import * as TestExchangeInternals from '../../generated-artifacts/TestExchangeInternals.json'; -import * as TestSignatureValidator from '../../generated-artifacts/TestSignatureValidator.json'; -import * as TestStaticCallReceiver from '../../generated-artifacts/TestStaticCallReceiver.json'; - -export const artifacts = { - AssetProxyOwner: AssetProxyOwner as ContractArtifact, - ERC20Proxy: ERC20Proxy as ContractArtifact, - ERC721Proxy: ERC721Proxy as ContractArtifact, - Exchange: Exchange as ContractArtifact, - MixinAuthorizable: MixinAuthorizable as ContractArtifact, - MultiAssetProxy: MultiAssetProxy as ContractArtifact, - TestAssetProxyDispatcher: TestAssetProxyDispatcher as ContractArtifact, - TestAssetProxyOwner: TestAssetProxyOwner as ContractArtifact, - TestExchangeInternals: TestExchangeInternals as ContractArtifact, - TestSignatureValidator: TestSignatureValidator as ContractArtifact, - TestStaticCallReceiver: TestStaticCallReceiver as ContractArtifact, -}; diff --git a/contracts/core/src/index.ts b/contracts/core/src/index.ts deleted file mode 100644 index ba813e7ca..000000000 --- a/contracts/core/src/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './artifacts'; -export * from './wrappers'; -export * from '../test/utils'; diff --git a/contracts/core/src/wrappers/index.ts b/contracts/core/src/wrappers/index.ts deleted file mode 100644 index 01b121054..000000000 --- a/contracts/core/src/wrappers/index.ts +++ /dev/null @@ -1,10 +0,0 @@ -export * from '../../generated-wrappers/asset_proxy_owner'; -export * from '../../generated-wrappers/erc20_proxy'; -export * from '../../generated-wrappers/erc721_proxy'; -export * from '../../generated-wrappers/exchange'; -export * from '../../generated-wrappers/mixin_authorizable'; -export * from '../../generated-wrappers/test_asset_proxy_dispatcher'; -export * from '../../generated-wrappers/test_asset_proxy_owner'; -export * from '../../generated-wrappers/test_exchange_internals'; -export * from '../../generated-wrappers/test_signature_validator'; -export * from '../../generated-wrappers/test_static_call_receiver'; diff --git a/contracts/core/test/asset_proxy/authorizable.ts b/contracts/core/test/asset_proxy/authorizable.ts deleted file mode 100644 index 853d18be0..000000000 --- a/contracts/core/test/asset_proxy/authorizable.ts +++ /dev/null @@ -1,211 +0,0 @@ -import { - chaiSetup, - constants, - expectTransactionFailedAsync, - provider, - txDefaults, - web3Wrapper, -} from '@0x/contracts-test-utils'; -import { BlockchainLifecycle } from '@0x/dev-utils'; -import { RevertReason } from '@0x/types'; -import { BigNumber } from '@0x/utils'; -import * as chai from 'chai'; -import * as _ from 'lodash'; - -import { MixinAuthorizableContract } from '../../generated-wrappers/mixin_authorizable'; -import { artifacts } from '../../src/artifacts'; - -chaiSetup.configure(); -const expect = chai.expect; -const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); - -describe('Authorizable', () => { - let owner: string; - let notOwner: string; - let address: string; - let authorizable: MixinAuthorizableContract; - - before(async () => { - await blockchainLifecycle.startAsync(); - }); - after(async () => { - await blockchainLifecycle.revertAsync(); - }); - before(async () => { - const accounts = await web3Wrapper.getAvailableAddressesAsync(); - [owner, address, notOwner] = _.slice(accounts, 0, 3); - authorizable = await MixinAuthorizableContract.deployFrom0xArtifactAsync( - artifacts.MixinAuthorizable, - provider, - txDefaults, - ); - }); - beforeEach(async () => { - await blockchainLifecycle.startAsync(); - }); - afterEach(async () => { - await blockchainLifecycle.revertAsync(); - }); - describe('addAuthorizedAddress', () => { - it('should throw if not called by owner', async () => { - return expectTransactionFailedAsync( - authorizable.addAuthorizedAddress.sendTransactionAsync(notOwner, { from: notOwner }), - RevertReason.OnlyContractOwner, - ); - }); - it('should allow owner to add an authorized address', async () => { - await web3Wrapper.awaitTransactionSuccessAsync( - await authorizable.addAuthorizedAddress.sendTransactionAsync(address, { from: owner }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - const isAuthorized = await authorizable.authorized.callAsync(address); - expect(isAuthorized).to.be.true(); - }); - it('should throw if owner attempts to authorize a duplicate address', async () => { - await web3Wrapper.awaitTransactionSuccessAsync( - await authorizable.addAuthorizedAddress.sendTransactionAsync(address, { from: owner }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - return expectTransactionFailedAsync( - authorizable.addAuthorizedAddress.sendTransactionAsync(address, { from: owner }), - RevertReason.TargetAlreadyAuthorized, - ); - }); - }); - - describe('removeAuthorizedAddress', () => { - it('should throw if not called by owner', async () => { - await web3Wrapper.awaitTransactionSuccessAsync( - await authorizable.addAuthorizedAddress.sendTransactionAsync(address, { from: owner }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - return expectTransactionFailedAsync( - authorizable.removeAuthorizedAddress.sendTransactionAsync(address, { - from: notOwner, - }), - RevertReason.OnlyContractOwner, - ); - }); - - it('should allow owner to remove an authorized address', async () => { - await web3Wrapper.awaitTransactionSuccessAsync( - await authorizable.addAuthorizedAddress.sendTransactionAsync(address, { from: owner }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - await web3Wrapper.awaitTransactionSuccessAsync( - await authorizable.removeAuthorizedAddress.sendTransactionAsync(address, { - from: owner, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - const isAuthorized = await authorizable.authorized.callAsync(address); - expect(isAuthorized).to.be.false(); - }); - - it('should throw if owner attempts to remove an address that is not authorized', async () => { - return expectTransactionFailedAsync( - authorizable.removeAuthorizedAddress.sendTransactionAsync(address, { - from: owner, - }), - RevertReason.TargetNotAuthorized, - ); - }); - }); - - describe('removeAuthorizedAddressAtIndex', () => { - it('should throw if not called by owner', async () => { - await web3Wrapper.awaitTransactionSuccessAsync( - await authorizable.addAuthorizedAddress.sendTransactionAsync(address, { from: owner }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - const index = new BigNumber(0); - return expectTransactionFailedAsync( - authorizable.removeAuthorizedAddressAtIndex.sendTransactionAsync(address, index, { - from: notOwner, - }), - RevertReason.OnlyContractOwner, - ); - }); - it('should throw if index is >= authorities.length', async () => { - await web3Wrapper.awaitTransactionSuccessAsync( - await authorizable.addAuthorizedAddress.sendTransactionAsync(address, { from: owner }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - const index = new BigNumber(1); - return expectTransactionFailedAsync( - authorizable.removeAuthorizedAddressAtIndex.sendTransactionAsync(address, index, { - from: owner, - }), - RevertReason.IndexOutOfBounds, - ); - }); - it('should throw if owner attempts to remove an address that is not authorized', async () => { - const index = new BigNumber(0); - return expectTransactionFailedAsync( - authorizable.removeAuthorizedAddressAtIndex.sendTransactionAsync(address, index, { - from: owner, - }), - RevertReason.TargetNotAuthorized, - ); - }); - it('should throw if address at index does not match target', async () => { - const address1 = address; - const address2 = notOwner; - await web3Wrapper.awaitTransactionSuccessAsync( - await authorizable.addAuthorizedAddress.sendTransactionAsync(address1, { from: owner }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - await web3Wrapper.awaitTransactionSuccessAsync( - await authorizable.addAuthorizedAddress.sendTransactionAsync(address2, { from: owner }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - const address1Index = new BigNumber(0); - return expectTransactionFailedAsync( - authorizable.removeAuthorizedAddressAtIndex.sendTransactionAsync(address2, address1Index, { - from: owner, - }), - RevertReason.AuthorizedAddressMismatch, - ); - }); - it('should allow owner to remove an authorized address', async () => { - await web3Wrapper.awaitTransactionSuccessAsync( - await authorizable.addAuthorizedAddress.sendTransactionAsync(address, { from: owner }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - const index = new BigNumber(0); - await web3Wrapper.awaitTransactionSuccessAsync( - await authorizable.removeAuthorizedAddressAtIndex.sendTransactionAsync(address, index, { - from: owner, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - const isAuthorized = await authorizable.authorized.callAsync(address); - expect(isAuthorized).to.be.false(); - }); - }); - - describe('getAuthorizedAddresses', () => { - it('should return all authorized addresses', async () => { - const initial = await authorizable.getAuthorizedAddresses.callAsync(); - expect(initial).to.have.length(0); - await web3Wrapper.awaitTransactionSuccessAsync( - await authorizable.addAuthorizedAddress.sendTransactionAsync(address, { - from: owner, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - const afterAdd = await authorizable.getAuthorizedAddresses.callAsync(); - expect(afterAdd).to.have.length(1); - expect(afterAdd).to.include(address); - - await web3Wrapper.awaitTransactionSuccessAsync( - await authorizable.removeAuthorizedAddress.sendTransactionAsync(address, { - from: owner, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - const afterRemove = await authorizable.getAuthorizedAddresses.callAsync(); - expect(afterRemove).to.have.length(0); - }); - }); -}); diff --git a/contracts/core/test/asset_proxy/proxies.ts b/contracts/core/test/asset_proxy/proxies.ts deleted file mode 100644 index c4bd95905..000000000 --- a/contracts/core/test/asset_proxy/proxies.ts +++ /dev/null @@ -1,1253 +0,0 @@ -import { artifacts as interfacesArtifacts, IAssetDataContract, IAssetProxyContract } from '@0x/contracts-interfaces'; -import { - chaiSetup, - constants, - expectTransactionFailedAsync, - expectTransactionFailedWithoutReasonAsync, - LogDecoder, - provider, - txDefaults, - web3Wrapper, -} from '@0x/contracts-test-utils'; -import { - artifacts as tokensArtifacts, - DummyERC20TokenContract, - DummyERC721ReceiverContract, - DummyERC721TokenContract, - DummyMultipleReturnERC20TokenContract, - DummyNoReturnERC20TokenContract, -} from '@0x/contracts-tokens'; -import { BlockchainLifecycle } from '@0x/dev-utils'; -import { assetDataUtils } from '@0x/order-utils'; -import { RevertReason } from '@0x/types'; -import { BigNumber } from '@0x/utils'; -import * as chai from 'chai'; -import * as _ from 'lodash'; - -import { ERC20ProxyContract } from '../../generated-wrappers/erc20_proxy'; -import { ERC721ProxyContract } from '../../generated-wrappers/erc721_proxy'; -import { MultiAssetProxyContract } from '../../generated-wrappers/multi_asset_proxy'; -import { artifacts } from '../../src/artifacts'; -import { ERC20Wrapper } from '../utils/erc20_wrapper'; -import { ERC721Wrapper } from '../utils/erc721_wrapper'; - -chaiSetup.configure(); -const expect = chai.expect; -const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); -const assetProxyInterface = new IAssetProxyContract( - interfacesArtifacts.IAssetProxy.compilerOutput.abi, - constants.NULL_ADDRESS, - provider, -); -const assetDataInterface = new IAssetDataContract( - interfacesArtifacts.IAssetData.compilerOutput.abi, - constants.NULL_ADDRESS, - provider, -); - -// tslint:disable:no-unnecessary-type-assertion -describe('Asset Transfer Proxies', () => { - let owner: string; - let notAuthorized: string; - let authorized: string; - let fromAddress: string; - let toAddress: string; - - let erc20TokenA: DummyERC20TokenContract; - let erc20TokenB: DummyERC20TokenContract; - let erc721TokenA: DummyERC721TokenContract; - let erc721TokenB: DummyERC721TokenContract; - let erc721Receiver: DummyERC721ReceiverContract; - let erc20Proxy: ERC20ProxyContract; - let erc721Proxy: ERC721ProxyContract; - let noReturnErc20Token: DummyNoReturnERC20TokenContract; - let multipleReturnErc20Token: DummyMultipleReturnERC20TokenContract; - let multiAssetProxy: MultiAssetProxyContract; - - let erc20Wrapper: ERC20Wrapper; - let erc721Wrapper: ERC721Wrapper; - let erc721AFromTokenId: BigNumber; - let erc721BFromTokenId: BigNumber; - - before(async () => { - await blockchainLifecycle.startAsync(); - }); - after(async () => { - await blockchainLifecycle.revertAsync(); - }); - before(async () => { - const accounts = await web3Wrapper.getAvailableAddressesAsync(); - const usedAddresses = ([owner, notAuthorized, authorized, fromAddress, toAddress] = _.slice(accounts, 0, 5)); - - erc20Wrapper = new ERC20Wrapper(provider, usedAddresses, owner); - erc721Wrapper = new ERC721Wrapper(provider, usedAddresses, owner); - - // Deploy AssetProxies - erc20Proxy = await erc20Wrapper.deployProxyAsync(); - erc721Proxy = await erc721Wrapper.deployProxyAsync(); - multiAssetProxy = await MultiAssetProxyContract.deployFrom0xArtifactAsync( - artifacts.MultiAssetProxy, - provider, - txDefaults, - ); - - // Configure ERC20Proxy - await web3Wrapper.awaitTransactionSuccessAsync( - await erc20Proxy.addAuthorizedAddress.sendTransactionAsync(authorized, { - from: owner, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - await web3Wrapper.awaitTransactionSuccessAsync( - await erc20Proxy.addAuthorizedAddress.sendTransactionAsync(multiAssetProxy.address, { - from: owner, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - - // Configure ERC721Proxy - await web3Wrapper.awaitTransactionSuccessAsync( - await erc721Proxy.addAuthorizedAddress.sendTransactionAsync(authorized, { - from: owner, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - await web3Wrapper.awaitTransactionSuccessAsync( - await erc721Proxy.addAuthorizedAddress.sendTransactionAsync(multiAssetProxy.address, { - from: owner, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - - // Configure MultiAssetProxy - await web3Wrapper.awaitTransactionSuccessAsync( - await multiAssetProxy.addAuthorizedAddress.sendTransactionAsync(authorized, { - from: owner, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - await web3Wrapper.awaitTransactionSuccessAsync( - await multiAssetProxy.registerAssetProxy.sendTransactionAsync(erc20Proxy.address, { - from: owner, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - await web3Wrapper.awaitTransactionSuccessAsync( - await multiAssetProxy.registerAssetProxy.sendTransactionAsync(erc721Proxy.address, { - from: owner, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - - // Deploy and configure ERC20 tokens - const numDummyErc20ToDeploy = 2; - [erc20TokenA, erc20TokenB] = await erc20Wrapper.deployDummyTokensAsync( - numDummyErc20ToDeploy, - constants.DUMMY_TOKEN_DECIMALS, - ); - noReturnErc20Token = await DummyNoReturnERC20TokenContract.deployFrom0xArtifactAsync( - tokensArtifacts.DummyNoReturnERC20Token, - provider, - txDefaults, - constants.DUMMY_TOKEN_NAME, - constants.DUMMY_TOKEN_SYMBOL, - constants.DUMMY_TOKEN_DECIMALS, - constants.DUMMY_TOKEN_TOTAL_SUPPLY, - ); - multipleReturnErc20Token = await DummyMultipleReturnERC20TokenContract.deployFrom0xArtifactAsync( - tokensArtifacts.DummyMultipleReturnERC20Token, - provider, - txDefaults, - constants.DUMMY_TOKEN_NAME, - constants.DUMMY_TOKEN_SYMBOL, - constants.DUMMY_TOKEN_DECIMALS, - constants.DUMMY_TOKEN_TOTAL_SUPPLY, - ); - - await erc20Wrapper.setBalancesAndAllowancesAsync(); - await web3Wrapper.awaitTransactionSuccessAsync( - await noReturnErc20Token.setBalance.sendTransactionAsync(fromAddress, constants.INITIAL_ERC20_BALANCE), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - await web3Wrapper.awaitTransactionSuccessAsync( - await noReturnErc20Token.approve.sendTransactionAsync( - erc20Proxy.address, - constants.INITIAL_ERC20_ALLOWANCE, - { from: fromAddress }, - ), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - await web3Wrapper.awaitTransactionSuccessAsync( - await multipleReturnErc20Token.setBalance.sendTransactionAsync( - fromAddress, - constants.INITIAL_ERC20_BALANCE, - ), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - await web3Wrapper.awaitTransactionSuccessAsync( - await multipleReturnErc20Token.approve.sendTransactionAsync( - erc20Proxy.address, - constants.INITIAL_ERC20_ALLOWANCE, - { from: fromAddress }, - ), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - - // Deploy and configure ERC721 tokens and receiver - [erc721TokenA, erc721TokenB] = await erc721Wrapper.deployDummyTokensAsync(); - erc721Receiver = await DummyERC721ReceiverContract.deployFrom0xArtifactAsync( - tokensArtifacts.DummyERC721Receiver, - provider, - txDefaults, - ); - - await erc721Wrapper.setBalancesAndAllowancesAsync(); - const erc721Balances = await erc721Wrapper.getBalancesAsync(); - erc721AFromTokenId = erc721Balances[fromAddress][erc721TokenA.address][0]; - erc721BFromTokenId = erc721Balances[fromAddress][erc721TokenB.address][0]; - }); - beforeEach(async () => { - await blockchainLifecycle.startAsync(); - }); - afterEach(async () => { - await blockchainLifecycle.revertAsync(); - }); - - describe('ERC20Proxy', () => { - it('should revert if undefined function is called', async () => { - const undefinedSelector = '0x01020304'; - await expectTransactionFailedWithoutReasonAsync( - web3Wrapper.sendTransactionAsync({ - from: owner, - to: erc20Proxy.address, - value: constants.ZERO_AMOUNT, - data: undefinedSelector, - }), - ); - }); - it('should have an id of 0xf47261b0', async () => { - const proxyId = await erc20Proxy.getProxyId.callAsync(); - const expectedProxyId = '0xf47261b0'; - expect(proxyId).to.equal(expectedProxyId); - }); - describe('transferFrom', () => { - it('should successfully transfer tokens', async () => { - // Construct ERC20 asset data - const encodedAssetData = assetDataUtils.encodeERC20AssetData(erc20TokenA.address); - // Perform a transfer from fromAddress to toAddress - const erc20Balances = await erc20Wrapper.getBalancesAsync(); - const amount = new BigNumber(10); - const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( - encodedAssetData, - fromAddress, - toAddress, - amount, - ); - await web3Wrapper.awaitTransactionSuccessAsync( - await web3Wrapper.sendTransactionAsync({ - to: erc20Proxy.address, - data, - from: authorized, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - // Verify transfer was successful - const newBalances = await erc20Wrapper.getBalancesAsync(); - expect(newBalances[fromAddress][erc20TokenA.address]).to.be.bignumber.equal( - erc20Balances[fromAddress][erc20TokenA.address].minus(amount), - ); - expect(newBalances[toAddress][erc20TokenA.address]).to.be.bignumber.equal( - erc20Balances[toAddress][erc20TokenA.address].add(amount), - ); - }); - - it('should successfully transfer tokens that do not return a value', async () => { - // Construct ERC20 asset data - const encodedAssetData = assetDataUtils.encodeERC20AssetData(noReturnErc20Token.address); - // Perform a transfer from fromAddress to toAddress - const initialFromBalance = await noReturnErc20Token.balanceOf.callAsync(fromAddress); - const initialToBalance = await noReturnErc20Token.balanceOf.callAsync(toAddress); - const amount = new BigNumber(10); - const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( - encodedAssetData, - fromAddress, - toAddress, - amount, - ); - await web3Wrapper.awaitTransactionSuccessAsync( - await web3Wrapper.sendTransactionAsync({ - to: erc20Proxy.address, - data, - from: authorized, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - // Verify transfer was successful - const newFromBalance = await noReturnErc20Token.balanceOf.callAsync(fromAddress); - const newToBalance = await noReturnErc20Token.balanceOf.callAsync(toAddress); - expect(newFromBalance).to.be.bignumber.equal(initialFromBalance.minus(amount)); - expect(newToBalance).to.be.bignumber.equal(initialToBalance.plus(amount)); - }); - - it('should successfully transfer tokens and ignore extra assetData', async () => { - // Construct ERC20 asset data - const extraData = '0102030405060708'; - const encodedAssetData = `${assetDataUtils.encodeERC20AssetData(erc20TokenA.address)}${extraData}`; - // Perform a transfer from fromAddress to toAddress - const erc20Balances = await erc20Wrapper.getBalancesAsync(); - const amount = new BigNumber(10); - const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( - encodedAssetData, - fromAddress, - toAddress, - amount, - ); - await web3Wrapper.awaitTransactionSuccessAsync( - await web3Wrapper.sendTransactionAsync({ - to: erc20Proxy.address, - data, - from: authorized, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - // Verify transfer was successful - const newBalances = await erc20Wrapper.getBalancesAsync(); - expect(newBalances[fromAddress][erc20TokenA.address]).to.be.bignumber.equal( - erc20Balances[fromAddress][erc20TokenA.address].minus(amount), - ); - expect(newBalances[toAddress][erc20TokenA.address]).to.be.bignumber.equal( - erc20Balances[toAddress][erc20TokenA.address].add(amount), - ); - }); - - it('should do nothing if transferring 0 amount of a token', async () => { - // Construct ERC20 asset data - const encodedAssetData = assetDataUtils.encodeERC20AssetData(erc20TokenA.address); - // Perform a transfer from fromAddress to toAddress - const erc20Balances = await erc20Wrapper.getBalancesAsync(); - const amount = new BigNumber(0); - const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( - encodedAssetData, - fromAddress, - toAddress, - amount, - ); - await web3Wrapper.awaitTransactionSuccessAsync( - await web3Wrapper.sendTransactionAsync({ - to: erc20Proxy.address, - data, - from: authorized, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - // Verify transfer was successful - const newBalances = await erc20Wrapper.getBalancesAsync(); - expect(newBalances[fromAddress][erc20TokenA.address]).to.be.bignumber.equal( - erc20Balances[fromAddress][erc20TokenA.address], - ); - expect(newBalances[toAddress][erc20TokenA.address]).to.be.bignumber.equal( - erc20Balances[toAddress][erc20TokenA.address], - ); - }); - - it('should revert if allowances are too low', async () => { - // Construct ERC20 asset data - const encodedAssetData = assetDataUtils.encodeERC20AssetData(erc20TokenA.address); - // Create allowance less than transfer amount. Set allowance on proxy. - const allowance = new BigNumber(0); - const amount = new BigNumber(10); - const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( - encodedAssetData, - fromAddress, - toAddress, - amount, - ); - await web3Wrapper.awaitTransactionSuccessAsync( - await erc20TokenA.approve.sendTransactionAsync(erc20Proxy.address, allowance, { - from: fromAddress, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - const erc20Balances = await erc20Wrapper.getBalancesAsync(); - // Perform a transfer; expect this to fail. - await expectTransactionFailedAsync( - web3Wrapper.sendTransactionAsync({ - to: erc20Proxy.address, - data, - from: authorized, - }), - RevertReason.TransferFailed, - ); - const newBalances = await erc20Wrapper.getBalancesAsync(); - expect(newBalances).to.deep.equal(erc20Balances); - }); - - it('should revert if allowances are too low and token does not return a value', async () => { - // Construct ERC20 asset data - const encodedAssetData = assetDataUtils.encodeERC20AssetData(noReturnErc20Token.address); - // Create allowance less than transfer amount. Set allowance on proxy. - const allowance = new BigNumber(0); - const amount = new BigNumber(10); - const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( - encodedAssetData, - fromAddress, - toAddress, - amount, - ); - await web3Wrapper.awaitTransactionSuccessAsync( - await noReturnErc20Token.approve.sendTransactionAsync(erc20Proxy.address, allowance, { - from: fromAddress, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - const initialFromBalance = await noReturnErc20Token.balanceOf.callAsync(fromAddress); - const initialToBalance = await noReturnErc20Token.balanceOf.callAsync(toAddress); - // Perform a transfer; expect this to fail. - await expectTransactionFailedAsync( - web3Wrapper.sendTransactionAsync({ - to: erc20Proxy.address, - data, - from: authorized, - }), - RevertReason.TransferFailed, - ); - const newFromBalance = await noReturnErc20Token.balanceOf.callAsync(fromAddress); - const newToBalance = await noReturnErc20Token.balanceOf.callAsync(toAddress); - expect(newFromBalance).to.be.bignumber.equal(initialFromBalance); - expect(newToBalance).to.be.bignumber.equal(initialToBalance); - }); - - it('should revert if caller is not authorized', async () => { - // Construct ERC20 asset data - const encodedAssetData = assetDataUtils.encodeERC20AssetData(erc20TokenA.address); - // Perform a transfer from fromAddress to toAddress - const amount = new BigNumber(10); - const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( - encodedAssetData, - fromAddress, - toAddress, - amount, - ); - const erc20Balances = await erc20Wrapper.getBalancesAsync(); - await expectTransactionFailedAsync( - web3Wrapper.sendTransactionAsync({ - to: erc20Proxy.address, - data, - from: notAuthorized, - }), - RevertReason.SenderNotAuthorized, - ); - const newBalances = await erc20Wrapper.getBalancesAsync(); - expect(newBalances).to.deep.equal(erc20Balances); - }); - - it('should revert if token returns more than 32 bytes', async () => { - // Construct ERC20 asset data - const encodedAssetData = assetDataUtils.encodeERC20AssetData(multipleReturnErc20Token.address); - const amount = new BigNumber(10); - const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( - encodedAssetData, - fromAddress, - toAddress, - amount, - ); - const initialFromBalance = await multipleReturnErc20Token.balanceOf.callAsync(fromAddress); - const initialToBalance = await multipleReturnErc20Token.balanceOf.callAsync(toAddress); - // Perform a transfer; expect this to fail. - await expectTransactionFailedAsync( - web3Wrapper.sendTransactionAsync({ - to: erc20Proxy.address, - data, - from: authorized, - }), - RevertReason.TransferFailed, - ); - const newFromBalance = await multipleReturnErc20Token.balanceOf.callAsync(fromAddress); - const newToBalance = await multipleReturnErc20Token.balanceOf.callAsync(toAddress); - expect(newFromBalance).to.be.bignumber.equal(initialFromBalance); - expect(newToBalance).to.be.bignumber.equal(initialToBalance); - }); - }); - }); - - describe('ERC721Proxy', () => { - it('should revert if undefined function is called', async () => { - const undefinedSelector = '0x01020304'; - await expectTransactionFailedWithoutReasonAsync( - web3Wrapper.sendTransactionAsync({ - from: owner, - to: erc721Proxy.address, - value: constants.ZERO_AMOUNT, - data: undefinedSelector, - }), - ); - }); - it('should have an id of 0x02571792', async () => { - const proxyId = await erc721Proxy.getProxyId.callAsync(); - const expectedProxyId = '0x02571792'; - expect(proxyId).to.equal(expectedProxyId); - }); - describe('transferFrom', () => { - it('should successfully transfer tokens', async () => { - // Construct ERC721 asset data - const encodedAssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId); - // Verify pre-condition - const ownerFromAsset = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId); - expect(ownerFromAsset).to.be.equal(fromAddress); - // Perform a transfer from fromAddress to toAddress - const amount = new BigNumber(1); - const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( - encodedAssetData, - fromAddress, - toAddress, - amount, - ); - await web3Wrapper.awaitTransactionSuccessAsync( - await web3Wrapper.sendTransactionAsync({ - to: erc721Proxy.address, - data, - from: authorized, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - // Verify transfer was successful - const newOwnerFromAsset = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId); - expect(newOwnerFromAsset).to.be.bignumber.equal(toAddress); - }); - - it('should successfully transfer tokens and ignore extra assetData', async () => { - // Construct ERC721 asset data - const extraData = '0102030405060708'; - const encodedAssetData = `${assetDataUtils.encodeERC721AssetData( - erc721TokenA.address, - erc721AFromTokenId, - )}${extraData}`; - // Verify pre-condition - const ownerFromAsset = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId); - expect(ownerFromAsset).to.be.equal(fromAddress); - // Perform a transfer from fromAddress to toAddress - const amount = new BigNumber(1); - const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( - encodedAssetData, - fromAddress, - toAddress, - amount, - ); - await web3Wrapper.awaitTransactionSuccessAsync( - await web3Wrapper.sendTransactionAsync({ - to: erc721Proxy.address, - data, - from: authorized, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - // Verify transfer was successful - const newOwnerFromAsset = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId); - expect(newOwnerFromAsset).to.be.bignumber.equal(toAddress); - }); - - it('should not call onERC721Received when transferring to a smart contract', async () => { - // Construct ERC721 asset data - const encodedAssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId); - // Verify pre-condition - const ownerFromAsset = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId); - expect(ownerFromAsset).to.be.equal(fromAddress); - // Perform a transfer from fromAddress to toAddress - const amount = new BigNumber(1); - const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( - encodedAssetData, - fromAddress, - erc721Receiver.address, - amount, - ); - const logDecoder = new LogDecoder(web3Wrapper, { ...artifacts, ...tokensArtifacts }); - const tx = await logDecoder.getTxWithDecodedLogsAsync( - await web3Wrapper.sendTransactionAsync({ - to: erc721Proxy.address, - data, - from: authorized, - gas: constants.MAX_TRANSFER_FROM_GAS, - }), - ); - // Verify that no log was emitted by erc721 receiver - expect(tx.logs.length).to.be.equal(1); - // Verify transfer was successful - const newOwnerFromAsset = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId); - expect(newOwnerFromAsset).to.be.bignumber.equal(erc721Receiver.address); - }); - - it('should revert if transferring 0 amount of a token', async () => { - // Construct ERC721 asset data - const encodedAssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId); - // Verify pre-condition - const ownerFromAsset = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId); - expect(ownerFromAsset).to.be.equal(fromAddress); - // Perform a transfer from fromAddress to toAddress - const amount = new BigNumber(0); - const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( - encodedAssetData, - fromAddress, - toAddress, - amount, - ); - await expectTransactionFailedAsync( - web3Wrapper.sendTransactionAsync({ - to: erc721Proxy.address, - data, - from: authorized, - }), - RevertReason.InvalidAmount, - ); - const newOwner = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId); - expect(newOwner).to.be.equal(ownerFromAsset); - }); - - it('should revert if transferring > 1 amount of a token', async () => { - // Construct ERC721 asset data - const encodedAssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId); - // Verify pre-condition - const ownerFromAsset = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId); - expect(ownerFromAsset).to.be.equal(fromAddress); - // Perform a transfer from fromAddress to toAddress - const amount = new BigNumber(500); - const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( - encodedAssetData, - fromAddress, - toAddress, - amount, - ); - await expectTransactionFailedAsync( - web3Wrapper.sendTransactionAsync({ - to: erc721Proxy.address, - data, - from: authorized, - }), - RevertReason.InvalidAmount, - ); - const newOwner = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId); - expect(newOwner).to.be.equal(ownerFromAsset); - }); - - it('should revert if allowances are too low', async () => { - // Construct ERC721 asset data - const encodedAssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId); - // Verify pre-condition - const ownerFromAsset = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId); - expect(ownerFromAsset).to.be.equal(fromAddress); - // Remove transfer approval for fromAddress. - await web3Wrapper.awaitTransactionSuccessAsync( - await erc721TokenA.approve.sendTransactionAsync(constants.NULL_ADDRESS, erc721AFromTokenId, { - from: fromAddress, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - // Perform a transfer; expect this to fail. - const amount = new BigNumber(1); - const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( - encodedAssetData, - fromAddress, - toAddress, - amount, - ); - await expectTransactionFailedAsync( - web3Wrapper.sendTransactionAsync({ - to: erc721Proxy.address, - data, - from: authorized, - }), - RevertReason.TransferFailed, - ); - const newOwner = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId); - expect(newOwner).to.be.equal(ownerFromAsset); - }); - - it('should revert if caller is not authorized', async () => { - // Construct ERC721 asset data - const encodedAssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId); - // Verify pre-condition - const ownerFromAsset = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId); - expect(ownerFromAsset).to.be.equal(fromAddress); - // Perform a transfer from fromAddress to toAddress - const amount = new BigNumber(1); - const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( - encodedAssetData, - fromAddress, - toAddress, - amount, - ); - await expectTransactionFailedAsync( - web3Wrapper.sendTransactionAsync({ - to: erc721Proxy.address, - data, - from: notAuthorized, - }), - RevertReason.SenderNotAuthorized, - ); - const newOwner = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId); - expect(newOwner).to.be.equal(ownerFromAsset); - }); - }); - }); - describe('MultiAssetProxy', () => { - it('should revert if undefined function is called', async () => { - const undefinedSelector = '0x01020304'; - await expectTransactionFailedWithoutReasonAsync( - web3Wrapper.sendTransactionAsync({ - from: owner, - to: multiAssetProxy.address, - value: constants.ZERO_AMOUNT, - data: undefinedSelector, - }), - ); - }); - it('should have an id of 0x94cfcdd7', async () => { - const proxyId = await multiAssetProxy.getProxyId.callAsync(); - // first 4 bytes of `keccak256('MultiAsset(uint256[],bytes[])')` - const expectedProxyId = '0x94cfcdd7'; - expect(proxyId).to.equal(expectedProxyId); - }); - describe('transferFrom', () => { - it('should transfer a single ERC20 token', async () => { - const inputAmount = new BigNumber(1); - const erc20Amount = new BigNumber(10); - const erc20AssetData = assetDataUtils.encodeERC20AssetData(erc20TokenA.address); - const amounts = [erc20Amount]; - const nestedAssetData = [erc20AssetData]; - const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData); - const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( - assetData, - fromAddress, - toAddress, - inputAmount, - ); - const erc20Balances = await erc20Wrapper.getBalancesAsync(); - await web3Wrapper.awaitTransactionSuccessAsync( - await web3Wrapper.sendTransactionAsync({ - to: multiAssetProxy.address, - data, - from: authorized, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - const newBalances = await erc20Wrapper.getBalancesAsync(); - const totalAmount = inputAmount.times(erc20Amount); - expect(newBalances[fromAddress][erc20TokenA.address]).to.be.bignumber.equal( - erc20Balances[fromAddress][erc20TokenA.address].minus(totalAmount), - ); - expect(newBalances[toAddress][erc20TokenA.address]).to.be.bignumber.equal( - erc20Balances[toAddress][erc20TokenA.address].add(totalAmount), - ); - }); - it('should successfully transfer multiple of the same ERC20 token', async () => { - const inputAmount = new BigNumber(1); - const erc20Amount1 = new BigNumber(10); - const erc20Amount2 = new BigNumber(20); - const erc20AssetData1 = assetDataUtils.encodeERC20AssetData(erc20TokenA.address); - const erc20AssetData2 = assetDataUtils.encodeERC20AssetData(erc20TokenA.address); - const amounts = [erc20Amount1, erc20Amount2]; - const nestedAssetData = [erc20AssetData1, erc20AssetData2]; - const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData); - const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( - assetData, - fromAddress, - toAddress, - inputAmount, - ); - const erc20Balances = await erc20Wrapper.getBalancesAsync(); - await web3Wrapper.awaitTransactionSuccessAsync( - await web3Wrapper.sendTransactionAsync({ - to: multiAssetProxy.address, - data, - from: authorized, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - const newBalances = await erc20Wrapper.getBalancesAsync(); - const totalAmount = inputAmount.times(erc20Amount1).plus(inputAmount.times(erc20Amount2)); - expect(newBalances[fromAddress][erc20TokenA.address]).to.be.bignumber.equal( - erc20Balances[fromAddress][erc20TokenA.address].minus(totalAmount), - ); - expect(newBalances[toAddress][erc20TokenA.address]).to.be.bignumber.equal( - erc20Balances[toAddress][erc20TokenA.address].add(totalAmount), - ); - }); - it('should successfully transfer multiple different ERC20 tokens', async () => { - const inputAmount = new BigNumber(1); - const erc20Amount1 = new BigNumber(10); - const erc20Amount2 = new BigNumber(20); - const erc20AssetData1 = assetDataUtils.encodeERC20AssetData(erc20TokenA.address); - const erc20AssetData2 = assetDataUtils.encodeERC20AssetData(erc20TokenB.address); - const amounts = [erc20Amount1, erc20Amount2]; - const nestedAssetData = [erc20AssetData1, erc20AssetData2]; - const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData); - const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( - assetData, - fromAddress, - toAddress, - inputAmount, - ); - const erc20Balances = await erc20Wrapper.getBalancesAsync(); - await web3Wrapper.awaitTransactionSuccessAsync( - await web3Wrapper.sendTransactionAsync({ - to: multiAssetProxy.address, - data, - from: authorized, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - const newBalances = await erc20Wrapper.getBalancesAsync(); - const totalErc20AAmount = inputAmount.times(erc20Amount1); - const totalErc20BAmount = inputAmount.times(erc20Amount2); - expect(newBalances[fromAddress][erc20TokenA.address]).to.be.bignumber.equal( - erc20Balances[fromAddress][erc20TokenA.address].minus(totalErc20AAmount), - ); - expect(newBalances[toAddress][erc20TokenA.address]).to.be.bignumber.equal( - erc20Balances[toAddress][erc20TokenA.address].add(totalErc20AAmount), - ); - expect(newBalances[fromAddress][erc20TokenB.address]).to.be.bignumber.equal( - erc20Balances[fromAddress][erc20TokenB.address].minus(totalErc20BAmount), - ); - expect(newBalances[toAddress][erc20TokenB.address]).to.be.bignumber.equal( - erc20Balances[toAddress][erc20TokenB.address].add(totalErc20BAmount), - ); - }); - it('should transfer a single ERC721 token', async () => { - const inputAmount = new BigNumber(1); - const erc721Amount = new BigNumber(1); - const erc721AssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId); - const amounts = [erc721Amount]; - const nestedAssetData = [erc721AssetData]; - const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData); - const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( - assetData, - fromAddress, - toAddress, - inputAmount, - ); - const ownerFromAsset = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId); - expect(ownerFromAsset).to.be.equal(fromAddress); - await web3Wrapper.awaitTransactionSuccessAsync( - await web3Wrapper.sendTransactionAsync({ - to: multiAssetProxy.address, - data, - from: authorized, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - const newOwnerFromAsset = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId); - expect(newOwnerFromAsset).to.be.equal(toAddress); - }); - it('should successfully transfer multiple of the same ERC721 token', async () => { - const erc721Balances = await erc721Wrapper.getBalancesAsync(); - const erc721AFromTokenId2 = erc721Balances[fromAddress][erc721TokenA.address][1]; - const erc721AssetData1 = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId); - const erc721AssetData2 = assetDataUtils.encodeERC721AssetData( - erc721TokenA.address, - erc721AFromTokenId2, - ); - const inputAmount = new BigNumber(1); - const erc721Amount = new BigNumber(1); - const amounts = [erc721Amount, erc721Amount]; - const nestedAssetData = [erc721AssetData1, erc721AssetData2]; - const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData); - const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( - assetData, - fromAddress, - toAddress, - inputAmount, - ); - const ownerFromAsset1 = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId); - expect(ownerFromAsset1).to.be.equal(fromAddress); - const ownerFromAsset2 = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId2); - expect(ownerFromAsset2).to.be.equal(fromAddress); - await web3Wrapper.awaitTransactionSuccessAsync( - await web3Wrapper.sendTransactionAsync({ - to: multiAssetProxy.address, - data, - from: authorized, - gas: constants.MAX_TRANSFER_FROM_GAS, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - const newOwnerFromAsset1 = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId); - const newOwnerFromAsset2 = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId2); - expect(newOwnerFromAsset1).to.be.equal(toAddress); - expect(newOwnerFromAsset2).to.be.equal(toAddress); - }); - it('should successfully transfer multiple different ERC721 tokens', async () => { - const erc721AssetData1 = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId); - const erc721AssetData2 = assetDataUtils.encodeERC721AssetData(erc721TokenB.address, erc721BFromTokenId); - const inputAmount = new BigNumber(1); - const erc721Amount = new BigNumber(1); - const amounts = [erc721Amount, erc721Amount]; - const nestedAssetData = [erc721AssetData1, erc721AssetData2]; - const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData); - const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( - assetData, - fromAddress, - toAddress, - inputAmount, - ); - const ownerFromAsset1 = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId); - expect(ownerFromAsset1).to.be.equal(fromAddress); - const ownerFromAsset2 = await erc721TokenB.ownerOf.callAsync(erc721BFromTokenId); - expect(ownerFromAsset2).to.be.equal(fromAddress); - await web3Wrapper.awaitTransactionSuccessAsync( - await web3Wrapper.sendTransactionAsync({ - to: multiAssetProxy.address, - data, - from: authorized, - gas: constants.MAX_TRANSFER_FROM_GAS, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - const newOwnerFromAsset1 = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId); - const newOwnerFromAsset2 = await erc721TokenB.ownerOf.callAsync(erc721BFromTokenId); - expect(newOwnerFromAsset1).to.be.equal(toAddress); - expect(newOwnerFromAsset2).to.be.equal(toAddress); - }); - it('should successfully transfer a combination of ERC20 and ERC721 tokens', async () => { - const inputAmount = new BigNumber(1); - const erc20Amount = new BigNumber(10); - const erc20AssetData = assetDataUtils.encodeERC20AssetData(erc20TokenA.address); - const erc721Amount = new BigNumber(1); - const erc721AssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId); - const amounts = [erc20Amount, erc721Amount]; - const nestedAssetData = [erc20AssetData, erc721AssetData]; - const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData); - const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( - assetData, - fromAddress, - toAddress, - inputAmount, - ); - const erc20Balances = await erc20Wrapper.getBalancesAsync(); - const ownerFromAsset = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId); - expect(ownerFromAsset).to.be.equal(fromAddress); - await web3Wrapper.awaitTransactionSuccessAsync( - await web3Wrapper.sendTransactionAsync({ - to: multiAssetProxy.address, - data, - from: authorized, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - const newBalances = await erc20Wrapper.getBalancesAsync(); - const totalAmount = inputAmount.times(erc20Amount); - expect(newBalances[fromAddress][erc20TokenA.address]).to.be.bignumber.equal( - erc20Balances[fromAddress][erc20TokenA.address].minus(totalAmount), - ); - expect(newBalances[toAddress][erc20TokenA.address]).to.be.bignumber.equal( - erc20Balances[toAddress][erc20TokenA.address].add(totalAmount), - ); - const newOwnerFromAsset = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId); - expect(newOwnerFromAsset).to.be.equal(toAddress); - }); - it('should successfully transfer tokens and ignore extra assetData', async () => { - const inputAmount = new BigNumber(1); - const erc20Amount = new BigNumber(10); - const erc20AssetData = assetDataUtils.encodeERC20AssetData(erc20TokenA.address); - const erc721Amount = new BigNumber(1); - const erc721AssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId); - const amounts = [erc20Amount, erc721Amount]; - const nestedAssetData = [erc20AssetData, erc721AssetData]; - const extraData = '0102030405060708'; - const assetData = `${assetDataInterface.MultiAsset.getABIEncodedTransactionData( - amounts, - nestedAssetData, - )}${extraData}`; - const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( - assetData, - fromAddress, - toAddress, - inputAmount, - ); - const erc20Balances = await erc20Wrapper.getBalancesAsync(); - const ownerFromAsset = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId); - expect(ownerFromAsset).to.be.equal(fromAddress); - await web3Wrapper.awaitTransactionSuccessAsync( - await web3Wrapper.sendTransactionAsync({ - to: multiAssetProxy.address, - data, - from: authorized, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - const newBalances = await erc20Wrapper.getBalancesAsync(); - const totalAmount = inputAmount.times(erc20Amount); - expect(newBalances[fromAddress][erc20TokenA.address]).to.be.bignumber.equal( - erc20Balances[fromAddress][erc20TokenA.address].minus(totalAmount), - ); - expect(newBalances[toAddress][erc20TokenA.address]).to.be.bignumber.equal( - erc20Balances[toAddress][erc20TokenA.address].add(totalAmount), - ); - const newOwnerFromAsset = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId); - expect(newOwnerFromAsset).to.be.equal(toAddress); - }); - it('should successfully transfer correct amounts when the `amount` > 1', async () => { - const inputAmount = new BigNumber(100); - const erc20Amount1 = new BigNumber(10); - const erc20Amount2 = new BigNumber(20); - const erc20AssetData1 = assetDataUtils.encodeERC20AssetData(erc20TokenA.address); - const erc20AssetData2 = assetDataUtils.encodeERC20AssetData(erc20TokenB.address); - const amounts = [erc20Amount1, erc20Amount2]; - const nestedAssetData = [erc20AssetData1, erc20AssetData2]; - const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData); - const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( - assetData, - fromAddress, - toAddress, - inputAmount, - ); - const erc20Balances = await erc20Wrapper.getBalancesAsync(); - await web3Wrapper.awaitTransactionSuccessAsync( - await web3Wrapper.sendTransactionAsync({ - to: multiAssetProxy.address, - data, - from: authorized, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - const newBalances = await erc20Wrapper.getBalancesAsync(); - const totalErc20AAmount = inputAmount.times(erc20Amount1); - const totalErc20BAmount = inputAmount.times(erc20Amount2); - expect(newBalances[fromAddress][erc20TokenA.address]).to.be.bignumber.equal( - erc20Balances[fromAddress][erc20TokenA.address].minus(totalErc20AAmount), - ); - expect(newBalances[toAddress][erc20TokenA.address]).to.be.bignumber.equal( - erc20Balances[toAddress][erc20TokenA.address].add(totalErc20AAmount), - ); - expect(newBalances[fromAddress][erc20TokenB.address]).to.be.bignumber.equal( - erc20Balances[fromAddress][erc20TokenB.address].minus(totalErc20BAmount), - ); - expect(newBalances[toAddress][erc20TokenB.address]).to.be.bignumber.equal( - erc20Balances[toAddress][erc20TokenB.address].add(totalErc20BAmount), - ); - }); - it('should successfully transfer a large amount of tokens', async () => { - const inputAmount = new BigNumber(1); - const erc20Amount1 = new BigNumber(10); - const erc20Amount2 = new BigNumber(20); - const erc20AssetData1 = assetDataUtils.encodeERC20AssetData(erc20TokenA.address); - const erc20AssetData2 = assetDataUtils.encodeERC20AssetData(erc20TokenB.address); - const erc721Amount = new BigNumber(1); - const erc721Balances = await erc721Wrapper.getBalancesAsync(); - const erc721AFromTokenId2 = erc721Balances[fromAddress][erc721TokenA.address][1]; - const erc721BFromTokenId2 = erc721Balances[fromAddress][erc721TokenB.address][1]; - const erc721AssetData1 = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId); - const erc721AssetData2 = assetDataUtils.encodeERC721AssetData( - erc721TokenA.address, - erc721AFromTokenId2, - ); - const erc721AssetData3 = assetDataUtils.encodeERC721AssetData(erc721TokenB.address, erc721BFromTokenId); - const erc721AssetData4 = assetDataUtils.encodeERC721AssetData( - erc721TokenB.address, - erc721BFromTokenId2, - ); - const amounts = [erc721Amount, erc20Amount1, erc721Amount, erc20Amount2, erc721Amount, erc721Amount]; - const nestedAssetData = [ - erc721AssetData1, - erc20AssetData1, - erc721AssetData2, - erc20AssetData2, - erc721AssetData3, - erc721AssetData4, - ]; - const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData); - const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( - assetData, - fromAddress, - toAddress, - inputAmount, - ); - const ownerFromAsset1 = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId); - expect(ownerFromAsset1).to.be.equal(fromAddress); - const ownerFromAsset2 = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId2); - expect(ownerFromAsset2).to.be.equal(fromAddress); - const ownerFromAsset3 = await erc721TokenB.ownerOf.callAsync(erc721BFromTokenId); - expect(ownerFromAsset3).to.be.equal(fromAddress); - const ownerFromAsset4 = await erc721TokenB.ownerOf.callAsync(erc721BFromTokenId2); - expect(ownerFromAsset4).to.be.equal(fromAddress); - const erc20Balances = await erc20Wrapper.getBalancesAsync(); - await web3Wrapper.awaitTransactionSuccessAsync( - await web3Wrapper.sendTransactionAsync({ - to: multiAssetProxy.address, - data, - from: authorized, - gas: constants.MAX_EXECUTE_TRANSACTION_GAS, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - const newOwnerFromAsset1 = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId); - const newOwnerFromAsset2 = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId2); - const newOwnerFromAsset3 = await erc721TokenB.ownerOf.callAsync(erc721BFromTokenId); - const newOwnerFromAsset4 = await erc721TokenB.ownerOf.callAsync(erc721BFromTokenId2); - expect(newOwnerFromAsset1).to.be.equal(toAddress); - expect(newOwnerFromAsset2).to.be.equal(toAddress); - expect(newOwnerFromAsset3).to.be.equal(toAddress); - expect(newOwnerFromAsset4).to.be.equal(toAddress); - const newBalances = await erc20Wrapper.getBalancesAsync(); - const totalErc20AAmount = inputAmount.times(erc20Amount1); - const totalErc20BAmount = inputAmount.times(erc20Amount2); - expect(newBalances[fromAddress][erc20TokenA.address]).to.be.bignumber.equal( - erc20Balances[fromAddress][erc20TokenA.address].minus(totalErc20AAmount), - ); - expect(newBalances[toAddress][erc20TokenA.address]).to.be.bignumber.equal( - erc20Balances[toAddress][erc20TokenA.address].add(totalErc20AAmount), - ); - expect(newBalances[fromAddress][erc20TokenB.address]).to.be.bignumber.equal( - erc20Balances[fromAddress][erc20TokenB.address].minus(totalErc20BAmount), - ); - expect(newBalances[toAddress][erc20TokenB.address]).to.be.bignumber.equal( - erc20Balances[toAddress][erc20TokenB.address].add(totalErc20BAmount), - ); - }); - it('should revert if a single transfer fails', async () => { - const inputAmount = new BigNumber(1); - const erc20Amount = new BigNumber(10); - const erc20AssetData = assetDataUtils.encodeERC20AssetData(erc20TokenA.address); - // 2 is an invalid erc721 amount - const erc721Amount = new BigNumber(2); - const erc721AssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId); - const amounts = [erc20Amount, erc721Amount]; - const nestedAssetData = [erc20AssetData, erc721AssetData]; - const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData); - const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( - assetData, - fromAddress, - toAddress, - inputAmount, - ); - await expectTransactionFailedAsync( - web3Wrapper.sendTransactionAsync({ - to: multiAssetProxy.address, - data, - from: authorized, - }), - RevertReason.InvalidAmount, - ); - }); - it('should revert if an AssetProxy is not registered', async () => { - const inputAmount = new BigNumber(1); - const erc20Amount = new BigNumber(10); - const erc20AssetData = assetDataUtils.encodeERC20AssetData(erc20TokenA.address); - const erc721Amount = new BigNumber(1); - const erc721AssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId); - const invalidProxyId = '0x12345678'; - const invalidErc721AssetData = `${invalidProxyId}${erc721AssetData.slice(10)}`; - const amounts = [erc20Amount, erc721Amount]; - const nestedAssetData = [erc20AssetData, invalidErc721AssetData]; - const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData); - const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( - assetData, - fromAddress, - toAddress, - inputAmount, - ); - await expectTransactionFailedAsync( - web3Wrapper.sendTransactionAsync({ - to: multiAssetProxy.address, - data, - from: authorized, - }), - RevertReason.AssetProxyDoesNotExist, - ); - }); - it('should revert if the length of `amounts` does not match the length of `nestedAssetData`', async () => { - const inputAmount = new BigNumber(1); - const erc20Amount = new BigNumber(10); - const erc20AssetData = assetDataUtils.encodeERC20AssetData(erc20TokenA.address); - const erc721AssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId); - const amounts = [erc20Amount]; - const nestedAssetData = [erc20AssetData, erc721AssetData]; - const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData); - const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( - assetData, - fromAddress, - toAddress, - inputAmount, - ); - await expectTransactionFailedAsync( - web3Wrapper.sendTransactionAsync({ - to: multiAssetProxy.address, - data, - from: authorized, - }), - RevertReason.LengthMismatch, - ); - }); - it('should revert if amounts multiplication results in an overflow', async () => { - const inputAmount = new BigNumber(2).pow(128); - const erc20Amount = new BigNumber(2).pow(128); - const erc20AssetData = assetDataUtils.encodeERC20AssetData(erc20TokenA.address); - const amounts = [erc20Amount]; - const nestedAssetData = [erc20AssetData]; - const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData); - const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( - assetData, - fromAddress, - toAddress, - inputAmount, - ); - await expectTransactionFailedAsync( - web3Wrapper.sendTransactionAsync({ - to: multiAssetProxy.address, - data, - from: authorized, - }), - RevertReason.Uint256Overflow, - ); - }); - it('should revert if an element of `nestedAssetData` is < 4 bytes long', async () => { - const inputAmount = new BigNumber(1); - const erc20Amount = new BigNumber(10); - const erc20AssetData = assetDataUtils.encodeERC20AssetData(erc20TokenA.address); - const erc721Amount = new BigNumber(1); - const erc721AssetData = '0x123456'; - const amounts = [erc20Amount, erc721Amount]; - const nestedAssetData = [erc20AssetData, erc721AssetData]; - const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData); - const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( - assetData, - fromAddress, - toAddress, - inputAmount, - ); - await expectTransactionFailedAsync( - web3Wrapper.sendTransactionAsync({ - to: multiAssetProxy.address, - data, - from: authorized, - }), - RevertReason.LengthGreaterThan3Required, - ); - }); - it('should revert if caller is not authorized', async () => { - const inputAmount = new BigNumber(1); - const erc20Amount = new BigNumber(10); - const erc20AssetData = assetDataUtils.encodeERC20AssetData(erc20TokenA.address); - const erc721Amount = new BigNumber(1); - const erc721AssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId); - const amounts = [erc20Amount, erc721Amount]; - const nestedAssetData = [erc20AssetData, erc721AssetData]; - const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData); - const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( - assetData, - fromAddress, - toAddress, - inputAmount, - ); - await expectTransactionFailedAsync( - web3Wrapper.sendTransactionAsync({ - to: multiAssetProxy.address, - data, - from: notAuthorized, - }), - RevertReason.SenderNotAuthorized, - ); - }); - }); - }); -}); -// tslint:enable:no-unnecessary-type-assertion -// tslint:disable:max-file-line-count diff --git a/contracts/core/test/exchange/core.ts b/contracts/core/test/exchange/core.ts deleted file mode 100644 index 9b948f991..000000000 --- a/contracts/core/test/exchange/core.ts +++ /dev/null @@ -1,1178 +0,0 @@ -import { artifacts as interfacesArtifacts, IAssetDataContract } from '@0x/contracts-interfaces'; -import { - chaiSetup, - constants, - ERC20BalancesByOwner, - expectTransactionFailedAsync, - getLatestBlockTimestampAsync, - increaseTimeAndMineBlockAsync, - OrderFactory, - OrderStatus, - provider, - txDefaults, - web3Wrapper, -} from '@0x/contracts-test-utils'; -import { - artifacts as tokensArtifacts, - DummyERC20TokenContract, - DummyERC20TokenTransferEventArgs, - DummyERC721TokenContract, - DummyNoReturnERC20TokenContract, - ReentrantERC20TokenContract, -} from '@0x/contracts-tokens'; -import { BlockchainLifecycle } from '@0x/dev-utils'; -import { assetDataUtils, orderHashUtils } from '@0x/order-utils'; -import { RevertReason, SignatureType, SignedOrder } from '@0x/types'; -import { BigNumber } from '@0x/utils'; -import { Web3Wrapper } from '@0x/web3-wrapper'; -import * as chai from 'chai'; -import { LogWithDecodedArgs } from 'ethereum-types'; -import ethUtil = require('ethereumjs-util'); -import * as _ from 'lodash'; - -import { ERC20ProxyContract } from '../../generated-wrappers/erc20_proxy'; -import { ERC721ProxyContract } from '../../generated-wrappers/erc721_proxy'; -import { ExchangeCancelEventArgs, ExchangeContract } from '../../generated-wrappers/exchange'; -import { MultiAssetProxyContract } from '../../generated-wrappers/multi_asset_proxy'; -import { TestStaticCallReceiverContract } from '../../generated-wrappers/test_static_call_receiver'; -import { artifacts } from '../../src/artifacts'; -import { ERC20Wrapper } from '../utils/erc20_wrapper'; -import { ERC721Wrapper } from '../utils/erc721_wrapper'; -import { ExchangeWrapper } from '../utils/exchange_wrapper'; - -chaiSetup.configure(); -const expect = chai.expect; -const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); -const assetDataInterface = new IAssetDataContract( - interfacesArtifacts.IAssetData.compilerOutput.abi, - constants.NULL_ADDRESS, - provider, -); -// tslint:disable:no-unnecessary-type-assertion -describe('Exchange core', () => { - let makerAddress: string; - let owner: string; - let takerAddress: string; - let feeRecipientAddress: string; - - let erc20TokenA: DummyERC20TokenContract; - let erc20TokenB: DummyERC20TokenContract; - let zrxToken: DummyERC20TokenContract; - let erc721Token: DummyERC721TokenContract; - let noReturnErc20Token: DummyNoReturnERC20TokenContract; - let reentrantErc20Token: ReentrantERC20TokenContract; - let exchange: ExchangeContract; - let erc20Proxy: ERC20ProxyContract; - let erc721Proxy: ERC721ProxyContract; - let multiAssetProxy: MultiAssetProxyContract; - let maliciousWallet: TestStaticCallReceiverContract; - let maliciousValidator: TestStaticCallReceiverContract; - - let signedOrder: SignedOrder; - let erc20Balances: ERC20BalancesByOwner; - let exchangeWrapper: ExchangeWrapper; - let erc20Wrapper: ERC20Wrapper; - let erc721Wrapper: ERC721Wrapper; - let orderFactory: OrderFactory; - - let erc721MakerAssetIds: BigNumber[]; - let erc721TakerAssetIds: BigNumber[]; - - let defaultMakerAssetAddress: string; - let defaultTakerAssetAddress: string; - - before(async () => { - await blockchainLifecycle.startAsync(); - }); - after(async () => { - await blockchainLifecycle.revertAsync(); - }); - before(async () => { - const accounts = await web3Wrapper.getAvailableAddressesAsync(); - const usedAddresses = ([owner, makerAddress, takerAddress, feeRecipientAddress] = _.slice(accounts, 0, 4)); - - erc20Wrapper = new ERC20Wrapper(provider, usedAddresses, owner); - erc721Wrapper = new ERC721Wrapper(provider, usedAddresses, owner); - - // Deploy AssetProxies, Exchange, tokens, and malicious contracts - erc20Proxy = await erc20Wrapper.deployProxyAsync(); - erc721Proxy = await erc721Wrapper.deployProxyAsync(); - multiAssetProxy = await MultiAssetProxyContract.deployFrom0xArtifactAsync( - artifacts.MultiAssetProxy, - provider, - txDefaults, - ); - const numDummyErc20ToDeploy = 3; - [erc20TokenA, erc20TokenB, zrxToken] = await erc20Wrapper.deployDummyTokensAsync( - numDummyErc20ToDeploy, - constants.DUMMY_TOKEN_DECIMALS, - ); - [erc721Token] = await erc721Wrapper.deployDummyTokensAsync(); - exchange = await ExchangeContract.deployFrom0xArtifactAsync( - artifacts.Exchange, - provider, - txDefaults, - assetDataUtils.encodeERC20AssetData(zrxToken.address), - ); - maliciousWallet = maliciousValidator = await TestStaticCallReceiverContract.deployFrom0xArtifactAsync( - artifacts.TestStaticCallReceiver, - provider, - txDefaults, - ); - reentrantErc20Token = await ReentrantERC20TokenContract.deployFrom0xArtifactAsync( - tokensArtifacts.ReentrantERC20Token, - provider, - txDefaults, - exchange.address, - ); - - // Configure ERC20Proxy - await web3Wrapper.awaitTransactionSuccessAsync( - await erc20Proxy.addAuthorizedAddress.sendTransactionAsync(exchange.address, { - from: owner, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - await web3Wrapper.awaitTransactionSuccessAsync( - await erc20Proxy.addAuthorizedAddress.sendTransactionAsync(multiAssetProxy.address, { - from: owner, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - - // Configure ERC721Proxy - await web3Wrapper.awaitTransactionSuccessAsync( - await erc721Proxy.addAuthorizedAddress.sendTransactionAsync(exchange.address, { - from: owner, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - await web3Wrapper.awaitTransactionSuccessAsync( - await erc721Proxy.addAuthorizedAddress.sendTransactionAsync(multiAssetProxy.address, { - from: owner, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - - // Configure MultiAssetProxy - await web3Wrapper.awaitTransactionSuccessAsync( - await multiAssetProxy.addAuthorizedAddress.sendTransactionAsync(exchange.address, { - from: owner, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - await web3Wrapper.awaitTransactionSuccessAsync( - await multiAssetProxy.registerAssetProxy.sendTransactionAsync(erc20Proxy.address, { - from: owner, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - await web3Wrapper.awaitTransactionSuccessAsync( - await multiAssetProxy.registerAssetProxy.sendTransactionAsync(erc721Proxy.address, { - from: owner, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - - // Configure Exchange - exchangeWrapper = new ExchangeWrapper(exchange, provider); - await exchangeWrapper.registerAssetProxyAsync(erc20Proxy.address, owner); - await exchangeWrapper.registerAssetProxyAsync(erc721Proxy.address, owner); - await exchangeWrapper.registerAssetProxyAsync(multiAssetProxy.address, owner); - - // Configure ERC20 tokens - await erc20Wrapper.setBalancesAndAllowancesAsync(); - - // Configure ERC721 tokens - await erc721Wrapper.setBalancesAndAllowancesAsync(); - const erc721Balances = await erc721Wrapper.getBalancesAsync(); - erc721MakerAssetIds = erc721Balances[makerAddress][erc721Token.address]; - erc721TakerAssetIds = erc721Balances[takerAddress][erc721Token.address]; - - // Configure order defaults - defaultMakerAssetAddress = erc20TokenA.address; - defaultTakerAssetAddress = erc20TokenB.address; - const defaultOrderParams = { - ...constants.STATIC_ORDER_PARAMS, - exchangeAddress: exchange.address, - makerAddress, - feeRecipientAddress, - makerAssetData: assetDataUtils.encodeERC20AssetData(defaultMakerAssetAddress), - takerAssetData: assetDataUtils.encodeERC20AssetData(defaultTakerAssetAddress), - }; - const privateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddress)]; - orderFactory = new OrderFactory(privateKey, defaultOrderParams); - }); - beforeEach(async () => { - await blockchainLifecycle.startAsync(); - }); - afterEach(async () => { - await blockchainLifecycle.revertAsync(); - }); - describe('fillOrder', () => { - beforeEach(async () => { - erc20Balances = await erc20Wrapper.getBalancesAsync(); - signedOrder = await orderFactory.newSignedOrderAsync(); - }); - - const reentrancyTest = (functionNames: string[]) => { - _.forEach(functionNames, async (functionName: string, functionId: number) => { - const description = `should not allow fillOrder to reenter the Exchange contract via ${functionName}`; - it(description, async () => { - signedOrder = await orderFactory.newSignedOrderAsync({ - makerAssetData: assetDataUtils.encodeERC20AssetData(reentrantErc20Token.address), - }); - await web3Wrapper.awaitTransactionSuccessAsync( - await reentrantErc20Token.setCurrentFunction.sendTransactionAsync(functionId), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - await expectTransactionFailedAsync( - exchangeWrapper.fillOrderAsync(signedOrder, takerAddress), - RevertReason.TransferFailed, - ); - }); - }); - }; - describe('fillOrder reentrancy tests', () => reentrancyTest(constants.FUNCTIONS_WITH_MUTEX)); - - it('should throw if signature is invalid', async () => { - signedOrder = await orderFactory.newSignedOrderAsync({ - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - }); - - const v = ethUtil.toBuffer(signedOrder.signature.slice(0, 4)); - const invalidR = ethUtil.sha3('invalidR'); - const invalidS = ethUtil.sha3('invalidS'); - const signatureType = ethUtil.toBuffer(`0x${signedOrder.signature.slice(-2)}`); - const invalidSigBuff = Buffer.concat([v, invalidR, invalidS, signatureType]); - const invalidSigHex = `0x${invalidSigBuff.toString('hex')}`; - signedOrder.signature = invalidSigHex; - return expectTransactionFailedAsync( - exchangeWrapper.fillOrderAsync(signedOrder, takerAddress), - RevertReason.InvalidOrderSignature, - ); - }); - - it('should throw if no value is filled', async () => { - signedOrder = await orderFactory.newSignedOrderAsync(); - await exchangeWrapper.fillOrderAsync(signedOrder, takerAddress); - return expectTransactionFailedAsync( - exchangeWrapper.fillOrderAsync(signedOrder, takerAddress), - RevertReason.OrderUnfillable, - ); - }); - - it('should revert if `isValidSignature` tries to update state when SignatureType=Wallet', async () => { - const maliciousMakerAddress = maliciousWallet.address; - await web3Wrapper.awaitTransactionSuccessAsync( - await erc20TokenA.setBalance.sendTransactionAsync( - maliciousMakerAddress, - constants.INITIAL_ERC20_BALANCE, - ), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - await web3Wrapper.awaitTransactionSuccessAsync( - await maliciousWallet.approveERC20.sendTransactionAsync( - erc20TokenA.address, - erc20Proxy.address, - constants.INITIAL_ERC20_ALLOWANCE, - ), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - signedOrder = await orderFactory.newSignedOrderAsync({ - makerAddress: maliciousMakerAddress, - makerFee: constants.ZERO_AMOUNT, - }); - signedOrder.signature = `0x0${SignatureType.Wallet}`; - await expectTransactionFailedAsync( - exchangeWrapper.fillOrderAsync(signedOrder, takerAddress), - RevertReason.WalletError, - ); - }); - - it('should revert if `isValidSignature` tries to update state when SignatureType=Validator', async () => { - const isApproved = true; - await web3Wrapper.awaitTransactionSuccessAsync( - await exchange.setSignatureValidatorApproval.sendTransactionAsync( - maliciousValidator.address, - isApproved, - { from: makerAddress }, - ), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - signedOrder.signature = `${maliciousValidator.address}0${SignatureType.Validator}`; - await expectTransactionFailedAsync( - exchangeWrapper.fillOrderAsync(signedOrder, takerAddress), - RevertReason.ValidatorError, - ); - }); - - it('should not emit transfer events for transfers where from == to', async () => { - const txReceipt = await exchangeWrapper.fillOrderAsync(signedOrder, makerAddress); - const logs = txReceipt.logs; - const transferLogs = _.filter( - logs, - log => (log as LogWithDecodedArgs<DummyERC20TokenTransferEventArgs>).event === 'Transfer', - ); - expect(transferLogs.length).to.be.equal(2); - expect((transferLogs[0] as LogWithDecodedArgs<DummyERC20TokenTransferEventArgs>).address).to.be.equal( - zrxToken.address, - ); - expect((transferLogs[0] as LogWithDecodedArgs<DummyERC20TokenTransferEventArgs>).args._from).to.be.equal( - makerAddress, - ); - expect((transferLogs[0] as LogWithDecodedArgs<DummyERC20TokenTransferEventArgs>).args._to).to.be.equal( - feeRecipientAddress, - ); - expect( - (transferLogs[0] as LogWithDecodedArgs<DummyERC20TokenTransferEventArgs>).args._value, - ).to.be.bignumber.equal(signedOrder.makerFee); - expect((transferLogs[1] as LogWithDecodedArgs<DummyERC20TokenTransferEventArgs>).address).to.be.equal( - zrxToken.address, - ); - expect((transferLogs[1] as LogWithDecodedArgs<DummyERC20TokenTransferEventArgs>).args._from).to.be.equal( - makerAddress, - ); - expect((transferLogs[1] as LogWithDecodedArgs<DummyERC20TokenTransferEventArgs>).args._to).to.be.equal( - feeRecipientAddress, - ); - expect( - (transferLogs[1] as LogWithDecodedArgs<DummyERC20TokenTransferEventArgs>).args._value, - ).to.be.bignumber.equal(signedOrder.takerFee); - }); - }); - - describe('Testing exchange of ERC20 tokens with no return values', () => { - before(async () => { - noReturnErc20Token = await DummyNoReturnERC20TokenContract.deployFrom0xArtifactAsync( - tokensArtifacts.DummyNoReturnERC20Token, - provider, - txDefaults, - constants.DUMMY_TOKEN_NAME, - constants.DUMMY_TOKEN_SYMBOL, - constants.DUMMY_TOKEN_DECIMALS, - constants.DUMMY_TOKEN_TOTAL_SUPPLY, - ); - await web3Wrapper.awaitTransactionSuccessAsync( - await noReturnErc20Token.setBalance.sendTransactionAsync(makerAddress, constants.INITIAL_ERC20_BALANCE), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - await web3Wrapper.awaitTransactionSuccessAsync( - await noReturnErc20Token.approve.sendTransactionAsync( - erc20Proxy.address, - constants.INITIAL_ERC20_ALLOWANCE, - { from: makerAddress }, - ), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - }); - it('should transfer the correct amounts when makerAssetAmount === takerAssetAmount', async () => { - signedOrder = await orderFactory.newSignedOrderAsync({ - makerAssetData: assetDataUtils.encodeERC20AssetData(noReturnErc20Token.address), - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18), - }); - - const initialMakerBalanceA = await noReturnErc20Token.balanceOf.callAsync(makerAddress); - const initialMakerBalanceB = await erc20TokenB.balanceOf.callAsync(makerAddress); - const initialMakerZrxBalance = await zrxToken.balanceOf.callAsync(makerAddress); - const initialTakerBalanceA = await noReturnErc20Token.balanceOf.callAsync(takerAddress); - const initialTakerBalanceB = await erc20TokenB.balanceOf.callAsync(takerAddress); - const initialTakerZrxBalance = await zrxToken.balanceOf.callAsync(takerAddress); - const initialFeeRecipientZrxBalance = await zrxToken.balanceOf.callAsync(feeRecipientAddress); - - await exchangeWrapper.fillOrderAsync(signedOrder, takerAddress); - - const finalMakerBalanceA = await noReturnErc20Token.balanceOf.callAsync(makerAddress); - const finalMakerBalanceB = await erc20TokenB.balanceOf.callAsync(makerAddress); - const finalMakerZrxBalance = await zrxToken.balanceOf.callAsync(makerAddress); - const finalTakerBalanceA = await noReturnErc20Token.balanceOf.callAsync(takerAddress); - const finalTakerBalanceB = await erc20TokenB.balanceOf.callAsync(takerAddress); - const finalTakerZrxBalance = await zrxToken.balanceOf.callAsync(takerAddress); - const finalFeeRecipientZrxBalance = await zrxToken.balanceOf.callAsync(feeRecipientAddress); - - expect(finalMakerBalanceA).to.be.bignumber.equal(initialMakerBalanceA.minus(signedOrder.makerAssetAmount)); - expect(finalMakerBalanceB).to.be.bignumber.equal(initialMakerBalanceB.plus(signedOrder.takerAssetAmount)); - expect(finalTakerBalanceA).to.be.bignumber.equal(initialTakerBalanceA.plus(signedOrder.makerAssetAmount)); - expect(finalTakerBalanceB).to.be.bignumber.equal(initialTakerBalanceB.minus(signedOrder.takerAssetAmount)); - expect(finalMakerZrxBalance).to.be.bignumber.equal(initialMakerZrxBalance.minus(signedOrder.makerFee)); - expect(finalTakerZrxBalance).to.be.bignumber.equal(initialTakerZrxBalance.minus(signedOrder.takerFee)); - expect(finalFeeRecipientZrxBalance).to.be.bignumber.equal( - initialFeeRecipientZrxBalance.plus(signedOrder.makerFee.plus(signedOrder.takerFee)), - ); - }); - it('should transfer the correct amounts when makerAssetAmount > takerAssetAmount', async () => { - signedOrder = await orderFactory.newSignedOrderAsync({ - makerAssetData: assetDataUtils.encodeERC20AssetData(noReturnErc20Token.address), - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(200), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18), - }); - - const initialMakerBalanceA = await noReturnErc20Token.balanceOf.callAsync(makerAddress); - const initialMakerBalanceB = await erc20TokenB.balanceOf.callAsync(makerAddress); - const initialMakerZrxBalance = await zrxToken.balanceOf.callAsync(makerAddress); - const initialTakerBalanceA = await noReturnErc20Token.balanceOf.callAsync(takerAddress); - const initialTakerBalanceB = await erc20TokenB.balanceOf.callAsync(takerAddress); - const initialTakerZrxBalance = await zrxToken.balanceOf.callAsync(takerAddress); - const initialFeeRecipientZrxBalance = await zrxToken.balanceOf.callAsync(feeRecipientAddress); - - await exchangeWrapper.fillOrderAsync(signedOrder, takerAddress); - - const finalMakerBalanceA = await noReturnErc20Token.balanceOf.callAsync(makerAddress); - const finalMakerBalanceB = await erc20TokenB.balanceOf.callAsync(makerAddress); - const finalMakerZrxBalance = await zrxToken.balanceOf.callAsync(makerAddress); - const finalTakerBalanceA = await noReturnErc20Token.balanceOf.callAsync(takerAddress); - const finalTakerBalanceB = await erc20TokenB.balanceOf.callAsync(takerAddress); - const finalTakerZrxBalance = await zrxToken.balanceOf.callAsync(takerAddress); - const finalFeeRecipientZrxBalance = await zrxToken.balanceOf.callAsync(feeRecipientAddress); - - expect(finalMakerBalanceA).to.be.bignumber.equal(initialMakerBalanceA.minus(signedOrder.makerAssetAmount)); - expect(finalMakerBalanceB).to.be.bignumber.equal(initialMakerBalanceB.plus(signedOrder.takerAssetAmount)); - expect(finalTakerBalanceA).to.be.bignumber.equal(initialTakerBalanceA.plus(signedOrder.makerAssetAmount)); - expect(finalTakerBalanceB).to.be.bignumber.equal(initialTakerBalanceB.minus(signedOrder.takerAssetAmount)); - expect(finalMakerZrxBalance).to.be.bignumber.equal(initialMakerZrxBalance.minus(signedOrder.makerFee)); - expect(finalTakerZrxBalance).to.be.bignumber.equal(initialTakerZrxBalance.minus(signedOrder.takerFee)); - expect(finalFeeRecipientZrxBalance).to.be.bignumber.equal( - initialFeeRecipientZrxBalance.plus(signedOrder.makerFee.plus(signedOrder.takerFee)), - ); - }); - it('should transfer the correct amounts when makerAssetAmount < takerAssetAmount', async () => { - signedOrder = await orderFactory.newSignedOrderAsync({ - makerAssetData: assetDataUtils.encodeERC20AssetData(noReturnErc20Token.address), - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(200), 18), - }); - - const initialMakerBalanceA = await noReturnErc20Token.balanceOf.callAsync(makerAddress); - const initialMakerBalanceB = await erc20TokenB.balanceOf.callAsync(makerAddress); - const initialMakerZrxBalance = await zrxToken.balanceOf.callAsync(makerAddress); - const initialTakerBalanceA = await noReturnErc20Token.balanceOf.callAsync(takerAddress); - const initialTakerBalanceB = await erc20TokenB.balanceOf.callAsync(takerAddress); - const initialTakerZrxBalance = await zrxToken.balanceOf.callAsync(takerAddress); - const initialFeeRecipientZrxBalance = await zrxToken.balanceOf.callAsync(feeRecipientAddress); - - await exchangeWrapper.fillOrderAsync(signedOrder, takerAddress); - - const finalMakerBalanceA = await noReturnErc20Token.balanceOf.callAsync(makerAddress); - const finalMakerBalanceB = await erc20TokenB.balanceOf.callAsync(makerAddress); - const finalMakerZrxBalance = await zrxToken.balanceOf.callAsync(makerAddress); - const finalTakerBalanceA = await noReturnErc20Token.balanceOf.callAsync(takerAddress); - const finalTakerBalanceB = await erc20TokenB.balanceOf.callAsync(takerAddress); - const finalTakerZrxBalance = await zrxToken.balanceOf.callAsync(takerAddress); - const finalFeeRecipientZrxBalance = await zrxToken.balanceOf.callAsync(feeRecipientAddress); - - expect(finalMakerBalanceA).to.be.bignumber.equal(initialMakerBalanceA.minus(signedOrder.makerAssetAmount)); - expect(finalMakerBalanceB).to.be.bignumber.equal(initialMakerBalanceB.plus(signedOrder.takerAssetAmount)); - expect(finalTakerBalanceA).to.be.bignumber.equal(initialTakerBalanceA.plus(signedOrder.makerAssetAmount)); - expect(finalTakerBalanceB).to.be.bignumber.equal(initialTakerBalanceB.minus(signedOrder.takerAssetAmount)); - expect(finalMakerZrxBalance).to.be.bignumber.equal(initialMakerZrxBalance.minus(signedOrder.makerFee)); - expect(finalTakerZrxBalance).to.be.bignumber.equal(initialTakerZrxBalance.minus(signedOrder.takerFee)); - expect(finalFeeRecipientZrxBalance).to.be.bignumber.equal( - initialFeeRecipientZrxBalance.plus(signedOrder.makerFee.plus(signedOrder.takerFee)), - ); - }); - }); - - describe('cancelOrder', () => { - beforeEach(async () => { - erc20Balances = await erc20Wrapper.getBalancesAsync(); - signedOrder = await orderFactory.newSignedOrderAsync(); - }); - - it('should throw if not sent by maker', async () => { - return expectTransactionFailedAsync( - exchangeWrapper.cancelOrderAsync(signedOrder, takerAddress), - RevertReason.InvalidMaker, - ); - }); - - it('should throw if makerAssetAmount is 0', async () => { - signedOrder = await orderFactory.newSignedOrderAsync({ - makerAssetAmount: new BigNumber(0), - }); - - return expectTransactionFailedAsync( - exchangeWrapper.cancelOrderAsync(signedOrder, makerAddress), - RevertReason.OrderUnfillable, - ); - }); - - it('should throw if takerAssetAmount is 0', async () => { - signedOrder = await orderFactory.newSignedOrderAsync({ - takerAssetAmount: new BigNumber(0), - }); - - return expectTransactionFailedAsync( - exchangeWrapper.cancelOrderAsync(signedOrder, makerAddress), - RevertReason.OrderUnfillable, - ); - }); - - it('should be able to cancel a full order', async () => { - await exchangeWrapper.cancelOrderAsync(signedOrder, makerAddress); - return expectTransactionFailedAsync( - exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { - takerAssetFillAmount: signedOrder.takerAssetAmount.div(2), - }), - RevertReason.OrderUnfillable, - ); - }); - - it('should log 1 event with correct arguments', async () => { - const res = await exchangeWrapper.cancelOrderAsync(signedOrder, makerAddress); - expect(res.logs).to.have.length(1); - - const log = res.logs[0] as LogWithDecodedArgs<ExchangeCancelEventArgs>; - const logArgs = log.args; - - expect(signedOrder.makerAddress).to.be.equal(logArgs.makerAddress); - expect(signedOrder.makerAddress).to.be.equal(logArgs.senderAddress); - expect(signedOrder.feeRecipientAddress).to.be.equal(logArgs.feeRecipientAddress); - expect(signedOrder.makerAssetData).to.be.equal(logArgs.makerAssetData); - expect(signedOrder.takerAssetData).to.be.equal(logArgs.takerAssetData); - expect(orderHashUtils.getOrderHashHex(signedOrder)).to.be.equal(logArgs.orderHash); - }); - - it('should throw if already cancelled', async () => { - await exchangeWrapper.cancelOrderAsync(signedOrder, makerAddress); - return expectTransactionFailedAsync( - exchangeWrapper.cancelOrderAsync(signedOrder, makerAddress), - RevertReason.OrderUnfillable, - ); - }); - - it('should throw if order is expired', async () => { - const currentTimestamp = await getLatestBlockTimestampAsync(); - signedOrder = await orderFactory.newSignedOrderAsync({ - expirationTimeSeconds: new BigNumber(currentTimestamp).sub(10), - }); - return expectTransactionFailedAsync( - exchangeWrapper.cancelOrderAsync(signedOrder, makerAddress), - RevertReason.OrderUnfillable, - ); - }); - - it('should throw if rounding error is greater than 0.1%', async () => { - signedOrder = await orderFactory.newSignedOrderAsync({ - makerAssetAmount: new BigNumber(1001), - takerAssetAmount: new BigNumber(3), - }); - - const fillTakerAssetAmount1 = new BigNumber(2); - await exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { - takerAssetFillAmount: fillTakerAssetAmount1, - }); - - const fillTakerAssetAmount2 = new BigNumber(1); - return expectTransactionFailedAsync( - exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { - takerAssetFillAmount: fillTakerAssetAmount2, - }), - RevertReason.RoundingError, - ); - }); - }); - - describe('cancelOrdersUpTo', () => { - it('should fail to set orderEpoch less than current orderEpoch', async () => { - const orderEpoch = new BigNumber(1); - await exchangeWrapper.cancelOrdersUpToAsync(orderEpoch, makerAddress); - const lesserOrderEpoch = new BigNumber(0); - return expectTransactionFailedAsync( - exchangeWrapper.cancelOrdersUpToAsync(lesserOrderEpoch, makerAddress), - RevertReason.InvalidNewOrderEpoch, - ); - }); - - it('should fail to set orderEpoch equal to existing orderEpoch', async () => { - const orderEpoch = new BigNumber(1); - await exchangeWrapper.cancelOrdersUpToAsync(orderEpoch, makerAddress); - return expectTransactionFailedAsync( - exchangeWrapper.cancelOrdersUpToAsync(orderEpoch, makerAddress), - RevertReason.InvalidNewOrderEpoch, - ); - }); - - it('should cancel only orders with a orderEpoch less than existing orderEpoch', async () => { - // Cancel all transactions with a orderEpoch less than 1 - const orderEpoch = new BigNumber(1); - await exchangeWrapper.cancelOrdersUpToAsync(orderEpoch, makerAddress); - - // Create 3 orders with orderEpoch values: 0,1,2,3 - // Since we cancelled with orderEpoch=1, orders with orderEpoch<=1 will not be processed - erc20Balances = await erc20Wrapper.getBalancesAsync(); - const signedOrders = [ - await orderFactory.newSignedOrderAsync({ - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(9), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(9), 18), - salt: new BigNumber(0), - }), - await orderFactory.newSignedOrderAsync({ - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(79), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(79), 18), - salt: new BigNumber(1), - }), - await orderFactory.newSignedOrderAsync({ - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(979), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(979), 18), - salt: new BigNumber(2), - }), - await orderFactory.newSignedOrderAsync({ - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(7979), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(7979), 18), - salt: new BigNumber(3), - }), - ]; - await exchangeWrapper.batchFillOrdersNoThrowAsync(signedOrders, takerAddress, { - // HACK(albrow): We need to hardcode the gas estimate here because - // the Geth gas estimator doesn't work with the way we use - // delegatecall and swallow errors. - gas: 600000, - }); - - const newBalances = await erc20Wrapper.getBalancesAsync(); - const fillMakerAssetAmount = signedOrders[2].makerAssetAmount.add(signedOrders[3].makerAssetAmount); - const fillTakerAssetAmount = signedOrders[2].takerAssetAmount.add(signedOrders[3].takerAssetAmount); - const makerFee = signedOrders[2].makerFee.add(signedOrders[3].makerFee); - const takerFee = signedOrders[2].takerFee.add(signedOrders[3].takerFee); - expect(newBalances[makerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[makerAddress][defaultMakerAssetAddress].minus(fillMakerAssetAmount), - ); - expect(newBalances[makerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[makerAddress][defaultTakerAssetAddress].add(fillTakerAssetAmount), - ); - expect(newBalances[makerAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[makerAddress][zrxToken.address].minus(makerFee), - ); - expect(newBalances[takerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[takerAddress][defaultTakerAssetAddress].minus(fillTakerAssetAmount), - ); - expect(newBalances[takerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[takerAddress][defaultMakerAssetAddress].add(fillMakerAssetAmount), - ); - expect(newBalances[takerAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[takerAddress][zrxToken.address].minus(takerFee), - ); - expect(newBalances[feeRecipientAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[feeRecipientAddress][zrxToken.address].add(makerFee.add(takerFee)), - ); - }); - }); - - describe('Testing Exchange of ERC721 Tokens', () => { - it('should throw when maker does not own the token with id makerAssetId', async () => { - // Construct Exchange parameters - const makerAssetId = erc721TakerAssetIds[0]; - const takerAssetId = erc721TakerAssetIds[1]; - signedOrder = await orderFactory.newSignedOrderAsync({ - makerAssetAmount: new BigNumber(1), - takerAssetAmount: new BigNumber(1), - makerAssetData: assetDataUtils.encodeERC721AssetData(erc721Token.address, makerAssetId), - takerAssetData: assetDataUtils.encodeERC721AssetData(erc721Token.address, takerAssetId), - }); - // Verify pre-conditions - const initialOwnerMakerAsset = await erc721Token.ownerOf.callAsync(makerAssetId); - expect(initialOwnerMakerAsset).to.be.bignumber.not.equal(makerAddress); - const initialOwnerTakerAsset = await erc721Token.ownerOf.callAsync(takerAssetId); - expect(initialOwnerTakerAsset).to.be.bignumber.equal(takerAddress); - // Call Exchange - const takerAssetFillAmount = signedOrder.takerAssetAmount; - return expectTransactionFailedAsync( - exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { takerAssetFillAmount }), - RevertReason.TransferFailed, - ); - }); - - it('should throw when taker does not own the token with id takerAssetId', async () => { - // Construct Exchange parameters - const makerAssetId = erc721MakerAssetIds[0]; - const takerAssetId = erc721MakerAssetIds[1]; - signedOrder = await orderFactory.newSignedOrderAsync({ - makerAssetAmount: new BigNumber(1), - takerAssetAmount: new BigNumber(1), - makerAssetData: assetDataUtils.encodeERC721AssetData(erc721Token.address, makerAssetId), - takerAssetData: assetDataUtils.encodeERC721AssetData(erc721Token.address, takerAssetId), - }); - // Verify pre-conditions - const initialOwnerMakerAsset = await erc721Token.ownerOf.callAsync(makerAssetId); - expect(initialOwnerMakerAsset).to.be.bignumber.equal(makerAddress); - const initialOwnerTakerAsset = await erc721Token.ownerOf.callAsync(takerAssetId); - expect(initialOwnerTakerAsset).to.be.bignumber.not.equal(takerAddress); - // Call Exchange - const takerAssetFillAmount = signedOrder.takerAssetAmount; - return expectTransactionFailedAsync( - exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { takerAssetFillAmount }), - RevertReason.TransferFailed, - ); - }); - - it('should throw when makerAssetAmount is greater than 1', async () => { - // Construct Exchange parameters - const makerAssetId = erc721MakerAssetIds[0]; - const takerAssetId = erc721TakerAssetIds[0]; - signedOrder = await orderFactory.newSignedOrderAsync({ - makerAssetAmount: new BigNumber(2), - takerAssetAmount: new BigNumber(1), - makerAssetData: assetDataUtils.encodeERC721AssetData(erc721Token.address, makerAssetId), - takerAssetData: assetDataUtils.encodeERC721AssetData(erc721Token.address, takerAssetId), - }); - // Verify pre-conditions - const initialOwnerMakerAsset = await erc721Token.ownerOf.callAsync(makerAssetId); - expect(initialOwnerMakerAsset).to.be.bignumber.equal(makerAddress); - const initialOwnerTakerAsset = await erc721Token.ownerOf.callAsync(takerAssetId); - expect(initialOwnerTakerAsset).to.be.bignumber.equal(takerAddress); - // Call Exchange - const takerAssetFillAmount = signedOrder.takerAssetAmount; - return expectTransactionFailedAsync( - exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { takerAssetFillAmount }), - RevertReason.InvalidAmount, - ); - }); - - it('should throw when takerAssetAmount is greater than 1', async () => { - // Construct Exchange parameters - const makerAssetId = erc721MakerAssetIds[0]; - const takerAssetId = erc721TakerAssetIds[0]; - signedOrder = await orderFactory.newSignedOrderAsync({ - makerAssetAmount: new BigNumber(1), - takerAssetAmount: new BigNumber(500), - makerAssetData: assetDataUtils.encodeERC721AssetData(erc721Token.address, makerAssetId), - takerAssetData: assetDataUtils.encodeERC721AssetData(erc721Token.address, takerAssetId), - }); - // Verify pre-conditions - const initialOwnerMakerAsset = await erc721Token.ownerOf.callAsync(makerAssetId); - expect(initialOwnerMakerAsset).to.be.bignumber.equal(makerAddress); - const initialOwnerTakerAsset = await erc721Token.ownerOf.callAsync(takerAssetId); - expect(initialOwnerTakerAsset).to.be.bignumber.equal(takerAddress); - // Call Exchange - const takerAssetFillAmount = signedOrder.takerAssetAmount; - return expectTransactionFailedAsync( - exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { takerAssetFillAmount }), - RevertReason.InvalidAmount, - ); - }); - - it('should throw on partial fill', async () => { - // Construct Exchange parameters - const makerAssetId = erc721MakerAssetIds[0]; - signedOrder = await orderFactory.newSignedOrderAsync({ - makerAssetAmount: new BigNumber(1), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18), - makerAssetData: assetDataUtils.encodeERC721AssetData(erc721Token.address, makerAssetId), - takerAssetData: assetDataUtils.encodeERC20AssetData(defaultTakerAssetAddress), - }); - // Call Exchange - const takerAssetFillAmount = signedOrder.takerAssetAmount.div(2); - return expectTransactionFailedAsync( - exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { takerAssetFillAmount }), - RevertReason.RoundingError, - ); - }); - }); - - describe('Testing exchange of multiple assets', () => { - it('should allow multiple assets to be exchanged for a single asset', async () => { - const makerAmounts = [new BigNumber(10), new BigNumber(20)]; - const makerNestedAssetData = [ - assetDataUtils.encodeERC20AssetData(erc20TokenA.address), - assetDataUtils.encodeERC20AssetData(erc20TokenB.address), - ]; - const makerAssetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData( - makerAmounts, - makerNestedAssetData, - ); - const makerAssetAmount = new BigNumber(1); - const takerAssetData = assetDataUtils.encodeERC20AssetData(zrxToken.address); - const takerAssetAmount = new BigNumber(10); - signedOrder = await orderFactory.newSignedOrderAsync({ - makerAssetData, - takerAssetData, - makerAssetAmount, - takerAssetAmount, - makerFee: constants.ZERO_AMOUNT, - takerFee: constants.ZERO_AMOUNT, - }); - - const initialMakerBalanceA = await erc20TokenA.balanceOf.callAsync(makerAddress); - const initialMakerBalanceB = await erc20TokenB.balanceOf.callAsync(makerAddress); - const initialMakerZrxBalance = await zrxToken.balanceOf.callAsync(makerAddress); - const initialTakerBalanceA = await erc20TokenA.balanceOf.callAsync(takerAddress); - const initialTakerBalanceB = await erc20TokenB.balanceOf.callAsync(takerAddress); - const initialTakerZrxBalance = await zrxToken.balanceOf.callAsync(takerAddress); - - await exchangeWrapper.fillOrderAsync(signedOrder, takerAddress); - - const finalMakerBalanceA = await erc20TokenA.balanceOf.callAsync(makerAddress); - const finalMakerBalanceB = await erc20TokenB.balanceOf.callAsync(makerAddress); - const finalMakerZrxBalance = await zrxToken.balanceOf.callAsync(makerAddress); - const finalTakerBalanceA = await erc20TokenA.balanceOf.callAsync(takerAddress); - const finalTakerBalanceB = await erc20TokenB.balanceOf.callAsync(takerAddress); - const finalTakerZrxBalance = await zrxToken.balanceOf.callAsync(takerAddress); - - expect(finalMakerBalanceA).to.be.bignumber.equal( - initialMakerBalanceA.minus(makerAmounts[0].times(makerAssetAmount)), - ); - expect(finalMakerBalanceB).to.be.bignumber.equal( - initialMakerBalanceB.minus(makerAmounts[1].times(makerAssetAmount)), - ); - expect(finalMakerZrxBalance).to.be.bignumber.equal(initialMakerZrxBalance.plus(takerAssetAmount)); - expect(finalTakerBalanceA).to.be.bignumber.equal( - initialTakerBalanceA.plus(makerAmounts[0].times(makerAssetAmount)), - ); - expect(finalTakerBalanceB).to.be.bignumber.equal( - initialTakerBalanceB.plus(makerAmounts[1].times(makerAssetAmount)), - ); - expect(finalTakerZrxBalance).to.be.bignumber.equal(initialTakerZrxBalance.minus(takerAssetAmount)); - }); - it('should allow multiple assets to be exchanged for multiple assets', async () => { - const makerAmounts = [new BigNumber(10), new BigNumber(20)]; - const makerNestedAssetData = [ - assetDataUtils.encodeERC20AssetData(erc20TokenA.address), - assetDataUtils.encodeERC20AssetData(erc20TokenB.address), - ]; - const makerAssetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData( - makerAmounts, - makerNestedAssetData, - ); - const makerAssetAmount = new BigNumber(1); - const takerAmounts = [new BigNumber(10), new BigNumber(1)]; - const takerAssetId = erc721TakerAssetIds[0]; - const takerNestedAssetData = [ - assetDataUtils.encodeERC20AssetData(zrxToken.address), - assetDataUtils.encodeERC721AssetData(erc721Token.address, takerAssetId), - ]; - const takerAssetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData( - takerAmounts, - takerNestedAssetData, - ); - const takerAssetAmount = new BigNumber(1); - signedOrder = await orderFactory.newSignedOrderAsync({ - makerAssetData, - takerAssetData, - makerAssetAmount, - takerAssetAmount, - makerFee: constants.ZERO_AMOUNT, - takerFee: constants.ZERO_AMOUNT, - }); - - const initialMakerBalanceA = await erc20TokenA.balanceOf.callAsync(makerAddress); - const initialMakerBalanceB = await erc20TokenB.balanceOf.callAsync(makerAddress); - const initialMakerZrxBalance = await zrxToken.balanceOf.callAsync(makerAddress); - const initialTakerBalanceA = await erc20TokenA.balanceOf.callAsync(takerAddress); - const initialTakerBalanceB = await erc20TokenB.balanceOf.callAsync(takerAddress); - const initialTakerZrxBalance = await zrxToken.balanceOf.callAsync(takerAddress); - const initialOwnerTakerAsset = await erc721Token.ownerOf.callAsync(takerAssetId); - expect(initialOwnerTakerAsset).to.be.bignumber.equal(takerAddress); - - await exchangeWrapper.fillOrderAsync(signedOrder, takerAddress); - - const finalMakerBalanceA = await erc20TokenA.balanceOf.callAsync(makerAddress); - const finalMakerBalanceB = await erc20TokenB.balanceOf.callAsync(makerAddress); - const finalMakerZrxBalance = await zrxToken.balanceOf.callAsync(makerAddress); - const finalTakerBalanceA = await erc20TokenA.balanceOf.callAsync(takerAddress); - const finalTakerBalanceB = await erc20TokenB.balanceOf.callAsync(takerAddress); - const finalTakerZrxBalance = await zrxToken.balanceOf.callAsync(takerAddress); - const finalOwnerTakerAsset = await erc721Token.ownerOf.callAsync(takerAssetId); - - expect(finalMakerBalanceA).to.be.bignumber.equal( - initialMakerBalanceA.minus(makerAmounts[0].times(makerAssetAmount)), - ); - expect(finalMakerBalanceB).to.be.bignumber.equal( - initialMakerBalanceB.minus(makerAmounts[1].times(makerAssetAmount)), - ); - expect(finalMakerZrxBalance).to.be.bignumber.equal( - initialMakerZrxBalance.plus(takerAmounts[0].times(takerAssetAmount)), - ); - expect(finalTakerBalanceA).to.be.bignumber.equal( - initialTakerBalanceA.plus(makerAmounts[0].times(makerAssetAmount)), - ); - expect(finalTakerBalanceB).to.be.bignumber.equal( - initialTakerBalanceB.plus(makerAmounts[1].times(makerAssetAmount)), - ); - expect(finalTakerZrxBalance).to.be.bignumber.equal( - initialTakerZrxBalance.minus(takerAmounts[0].times(takerAssetAmount)), - ); - expect(finalOwnerTakerAsset).to.be.equal(makerAddress); - }); - it('should allow an order selling multiple assets to be partially filled', async () => { - const makerAmounts = [new BigNumber(10), new BigNumber(20)]; - const makerNestedAssetData = [ - assetDataUtils.encodeERC20AssetData(erc20TokenA.address), - assetDataUtils.encodeERC20AssetData(erc20TokenB.address), - ]; - const makerAssetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData( - makerAmounts, - makerNestedAssetData, - ); - const makerAssetAmount = new BigNumber(30); - const takerAssetData = assetDataUtils.encodeERC20AssetData(zrxToken.address); - const takerAssetAmount = new BigNumber(10); - signedOrder = await orderFactory.newSignedOrderAsync({ - makerAssetData, - takerAssetData, - makerAssetAmount, - takerAssetAmount, - makerFee: constants.ZERO_AMOUNT, - takerFee: constants.ZERO_AMOUNT, - }); - - const initialMakerBalanceA = await erc20TokenA.balanceOf.callAsync(makerAddress); - const initialMakerBalanceB = await erc20TokenB.balanceOf.callAsync(makerAddress); - const initialMakerZrxBalance = await zrxToken.balanceOf.callAsync(makerAddress); - const initialTakerBalanceA = await erc20TokenA.balanceOf.callAsync(takerAddress); - const initialTakerBalanceB = await erc20TokenB.balanceOf.callAsync(takerAddress); - const initialTakerZrxBalance = await zrxToken.balanceOf.callAsync(takerAddress); - - const takerAssetFillAmount = takerAssetAmount.dividedToIntegerBy(2); - await exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { - takerAssetFillAmount, - }); - - const finalMakerBalanceA = await erc20TokenA.balanceOf.callAsync(makerAddress); - const finalMakerBalanceB = await erc20TokenB.balanceOf.callAsync(makerAddress); - const finalMakerZrxBalance = await zrxToken.balanceOf.callAsync(makerAddress); - const finalTakerBalanceA = await erc20TokenA.balanceOf.callAsync(takerAddress); - const finalTakerBalanceB = await erc20TokenB.balanceOf.callAsync(takerAddress); - const finalTakerZrxBalance = await zrxToken.balanceOf.callAsync(takerAddress); - - expect(finalMakerBalanceA).to.be.bignumber.equal( - initialMakerBalanceA.minus( - makerAmounts[0].times( - makerAssetAmount.times(takerAssetFillAmount).dividedToIntegerBy(takerAssetAmount), - ), - ), - ); - expect(finalMakerBalanceB).to.be.bignumber.equal( - initialMakerBalanceB.minus( - makerAmounts[1].times( - makerAssetAmount.times(takerAssetFillAmount).dividedToIntegerBy(takerAssetAmount), - ), - ), - ); - expect(finalMakerZrxBalance).to.be.bignumber.equal( - initialMakerZrxBalance.plus( - takerAssetAmount.times(takerAssetFillAmount).dividedToIntegerBy(takerAssetAmount), - ), - ); - expect(finalTakerBalanceA).to.be.bignumber.equal( - initialTakerBalanceA.plus( - makerAmounts[0].times( - makerAssetAmount.times(takerAssetFillAmount).dividedToIntegerBy(takerAssetAmount), - ), - ), - ); - expect(finalTakerBalanceB).to.be.bignumber.equal( - initialTakerBalanceB.plus( - makerAmounts[1].times( - makerAssetAmount.times(takerAssetFillAmount).dividedToIntegerBy(takerAssetAmount), - ), - ), - ); - expect(finalTakerZrxBalance).to.be.bignumber.equal( - initialTakerZrxBalance.minus( - takerAssetAmount.times(takerAssetFillAmount).dividedToIntegerBy(takerAssetAmount), - ), - ); - }); - it('should allow an order buying multiple assets to be partially filled', async () => { - const takerAmounts = [new BigNumber(10), new BigNumber(20)]; - const takerNestedAssetData = [ - assetDataUtils.encodeERC20AssetData(erc20TokenA.address), - assetDataUtils.encodeERC20AssetData(erc20TokenB.address), - ]; - const takerAssetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData( - takerAmounts, - takerNestedAssetData, - ); - const takerAssetAmount = new BigNumber(30); - const makerAssetData = assetDataUtils.encodeERC20AssetData(zrxToken.address); - const makerAssetAmount = new BigNumber(10); - signedOrder = await orderFactory.newSignedOrderAsync({ - makerAssetData, - takerAssetData, - makerAssetAmount, - takerAssetAmount, - makerFee: constants.ZERO_AMOUNT, - takerFee: constants.ZERO_AMOUNT, - }); - - const initialMakerBalanceA = await erc20TokenA.balanceOf.callAsync(makerAddress); - const initialMakerBalanceB = await erc20TokenB.balanceOf.callAsync(makerAddress); - const initialMakerZrxBalance = await zrxToken.balanceOf.callAsync(makerAddress); - const initialTakerBalanceA = await erc20TokenA.balanceOf.callAsync(takerAddress); - const initialTakerBalanceB = await erc20TokenB.balanceOf.callAsync(takerAddress); - const initialTakerZrxBalance = await zrxToken.balanceOf.callAsync(takerAddress); - - const takerAssetFillAmount = takerAssetAmount.dividedToIntegerBy(2); - await exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { - takerAssetFillAmount, - }); - - const finalMakerBalanceA = await erc20TokenA.balanceOf.callAsync(makerAddress); - const finalMakerBalanceB = await erc20TokenB.balanceOf.callAsync(makerAddress); - const finalMakerZrxBalance = await zrxToken.balanceOf.callAsync(makerAddress); - const finalTakerBalanceA = await erc20TokenA.balanceOf.callAsync(takerAddress); - const finalTakerBalanceB = await erc20TokenB.balanceOf.callAsync(takerAddress); - const finalTakerZrxBalance = await zrxToken.balanceOf.callAsync(takerAddress); - - expect(finalMakerBalanceA).to.be.bignumber.equal( - initialMakerBalanceA.plus( - takerAmounts[0].times( - takerAssetAmount.times(takerAssetFillAmount).dividedToIntegerBy(takerAssetAmount), - ), - ), - ); - expect(finalMakerBalanceB).to.be.bignumber.equal( - initialMakerBalanceB.plus( - takerAmounts[1].times( - takerAssetAmount.times(takerAssetFillAmount).dividedToIntegerBy(takerAssetAmount), - ), - ), - ); - expect(finalMakerZrxBalance).to.be.bignumber.equal( - initialMakerZrxBalance.minus( - makerAssetAmount.times(takerAssetFillAmount).dividedToIntegerBy(takerAssetAmount), - ), - ); - expect(finalTakerBalanceA).to.be.bignumber.equal( - initialTakerBalanceA.minus( - takerAmounts[0].times( - takerAssetAmount.times(takerAssetFillAmount).dividedToIntegerBy(takerAssetAmount), - ), - ), - ); - expect(finalTakerBalanceB).to.be.bignumber.equal( - initialTakerBalanceB.minus( - takerAmounts[1].times( - takerAssetAmount.times(takerAssetFillAmount).dividedToIntegerBy(takerAssetAmount), - ), - ), - ); - expect(finalTakerZrxBalance).to.be.bignumber.equal( - initialTakerZrxBalance.plus( - makerAssetAmount.times(takerAssetFillAmount).dividedToIntegerBy(takerAssetAmount), - ), - ); - }); - }); - - describe('getOrderInfo', () => { - beforeEach(async () => { - signedOrder = await orderFactory.newSignedOrderAsync(); - }); - it('should return the correct orderInfo for an unfilled valid order', async () => { - const orderInfo = await exchangeWrapper.getOrderInfoAsync(signedOrder); - const expectedOrderHash = orderHashUtils.getOrderHashHex(signedOrder); - const expectedTakerAssetFilledAmount = new BigNumber(0); - const expectedOrderStatus = OrderStatus.FILLABLE; - expect(orderInfo.orderHash).to.be.equal(expectedOrderHash); - expect(orderInfo.orderTakerAssetFilledAmount).to.be.bignumber.equal(expectedTakerAssetFilledAmount); - expect(orderInfo.orderStatus).to.equal(expectedOrderStatus); - }); - it('should return the correct orderInfo for a fully filled order', async () => { - await exchangeWrapper.fillOrderAsync(signedOrder, takerAddress); - const orderInfo = await exchangeWrapper.getOrderInfoAsync(signedOrder); - const expectedOrderHash = orderHashUtils.getOrderHashHex(signedOrder); - const expectedTakerAssetFilledAmount = signedOrder.takerAssetAmount; - const expectedOrderStatus = OrderStatus.FULLY_FILLED; - expect(orderInfo.orderHash).to.be.equal(expectedOrderHash); - expect(orderInfo.orderTakerAssetFilledAmount).to.be.bignumber.equal(expectedTakerAssetFilledAmount); - expect(orderInfo.orderStatus).to.equal(expectedOrderStatus); - }); - it('should return the correct orderInfo for a partially filled order', async () => { - const takerAssetFillAmount = signedOrder.takerAssetAmount.div(2); - await exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { takerAssetFillAmount }); - const orderInfo = await exchangeWrapper.getOrderInfoAsync(signedOrder); - const expectedOrderHash = orderHashUtils.getOrderHashHex(signedOrder); - const expectedTakerAssetFilledAmount = takerAssetFillAmount; - const expectedOrderStatus = OrderStatus.FILLABLE; - expect(orderInfo.orderHash).to.be.equal(expectedOrderHash); - expect(orderInfo.orderTakerAssetFilledAmount).to.be.bignumber.equal(expectedTakerAssetFilledAmount); - expect(orderInfo.orderStatus).to.equal(expectedOrderStatus); - }); - it('should return the correct orderInfo for a cancelled and unfilled order', async () => { - await exchangeWrapper.cancelOrderAsync(signedOrder, makerAddress); - const orderInfo = await exchangeWrapper.getOrderInfoAsync(signedOrder); - const expectedOrderHash = orderHashUtils.getOrderHashHex(signedOrder); - const expectedTakerAssetFilledAmount = new BigNumber(0); - const expectedOrderStatus = OrderStatus.CANCELLED; - expect(orderInfo.orderHash).to.be.equal(expectedOrderHash); - expect(orderInfo.orderTakerAssetFilledAmount).to.be.bignumber.equal(expectedTakerAssetFilledAmount); - expect(orderInfo.orderStatus).to.equal(expectedOrderStatus); - }); - it('should return the correct orderInfo for a cancelled and partially filled order', async () => { - const takerAssetFillAmount = signedOrder.takerAssetAmount.div(2); - await exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { takerAssetFillAmount }); - await exchangeWrapper.cancelOrderAsync(signedOrder, makerAddress); - const orderInfo = await exchangeWrapper.getOrderInfoAsync(signedOrder); - const expectedOrderHash = orderHashUtils.getOrderHashHex(signedOrder); - const expectedTakerAssetFilledAmount = takerAssetFillAmount; - const expectedOrderStatus = OrderStatus.CANCELLED; - expect(orderInfo.orderHash).to.be.equal(expectedOrderHash); - expect(orderInfo.orderTakerAssetFilledAmount).to.be.bignumber.equal(expectedTakerAssetFilledAmount); - expect(orderInfo.orderStatus).to.equal(expectedOrderStatus); - }); - it('should return the correct orderInfo for an expired and unfilled order', async () => { - const currentTimestamp = await getLatestBlockTimestampAsync(); - const timeUntilExpiration = signedOrder.expirationTimeSeconds.minus(currentTimestamp).toNumber(); - await increaseTimeAndMineBlockAsync(timeUntilExpiration); - const orderInfo = await exchangeWrapper.getOrderInfoAsync(signedOrder); - const expectedOrderHash = orderHashUtils.getOrderHashHex(signedOrder); - const expectedTakerAssetFilledAmount = new BigNumber(0); - const expectedOrderStatus = OrderStatus.EXPIRED; - expect(orderInfo.orderHash).to.be.equal(expectedOrderHash); - expect(orderInfo.orderTakerAssetFilledAmount).to.be.bignumber.equal(expectedTakerAssetFilledAmount); - expect(orderInfo.orderStatus).to.equal(expectedOrderStatus); - }); - it('should return the correct orderInfo for an expired and partially filled order', async () => { - const takerAssetFillAmount = signedOrder.takerAssetAmount.div(2); - await exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { takerAssetFillAmount }); - const currentTimestamp = await getLatestBlockTimestampAsync(); - const timeUntilExpiration = signedOrder.expirationTimeSeconds.minus(currentTimestamp).toNumber(); - await increaseTimeAndMineBlockAsync(timeUntilExpiration); - const orderInfo = await exchangeWrapper.getOrderInfoAsync(signedOrder); - const expectedOrderHash = orderHashUtils.getOrderHashHex(signedOrder); - const expectedTakerAssetFilledAmount = takerAssetFillAmount; - const expectedOrderStatus = OrderStatus.EXPIRED; - expect(orderInfo.orderHash).to.be.equal(expectedOrderHash); - expect(orderInfo.orderTakerAssetFilledAmount).to.be.bignumber.equal(expectedTakerAssetFilledAmount); - expect(orderInfo.orderStatus).to.equal(expectedOrderStatus); - }); - it('should return the correct orderInfo for an expired and fully filled order', async () => { - await exchangeWrapper.fillOrderAsync(signedOrder, takerAddress); - const currentTimestamp = await getLatestBlockTimestampAsync(); - const timeUntilExpiration = signedOrder.expirationTimeSeconds.minus(currentTimestamp).toNumber(); - await increaseTimeAndMineBlockAsync(timeUntilExpiration); - const orderInfo = await exchangeWrapper.getOrderInfoAsync(signedOrder); - const expectedOrderHash = orderHashUtils.getOrderHashHex(signedOrder); - const expectedTakerAssetFilledAmount = signedOrder.takerAssetAmount; - // FULLY_FILLED takes precedence over EXPIRED - const expectedOrderStatus = OrderStatus.FULLY_FILLED; - expect(orderInfo.orderHash).to.be.equal(expectedOrderHash); - expect(orderInfo.orderTakerAssetFilledAmount).to.be.bignumber.equal(expectedTakerAssetFilledAmount); - expect(orderInfo.orderStatus).to.equal(expectedOrderStatus); - }); - it('should return the correct orderInfo for an order with a makerAssetAmount of 0', async () => { - signedOrder = await orderFactory.newSignedOrderAsync({ makerAssetAmount: new BigNumber(0) }); - const orderInfo = await exchangeWrapper.getOrderInfoAsync(signedOrder); - const expectedOrderHash = orderHashUtils.getOrderHashHex(signedOrder); - const expectedTakerAssetFilledAmount = new BigNumber(0); - const expectedOrderStatus = OrderStatus.INVALID_MAKER_ASSET_AMOUNT; - expect(orderInfo.orderHash).to.be.equal(expectedOrderHash); - expect(orderInfo.orderTakerAssetFilledAmount).to.be.bignumber.equal(expectedTakerAssetFilledAmount); - expect(orderInfo.orderStatus).to.equal(expectedOrderStatus); - }); - it('should return the correct orderInfo for an order with a takerAssetAmount of 0', async () => { - signedOrder = await orderFactory.newSignedOrderAsync({ takerAssetAmount: new BigNumber(0) }); - const orderInfo = await exchangeWrapper.getOrderInfoAsync(signedOrder); - const expectedOrderHash = orderHashUtils.getOrderHashHex(signedOrder); - const expectedTakerAssetFilledAmount = new BigNumber(0); - const expectedOrderStatus = OrderStatus.INVALID_TAKER_ASSET_AMOUNT; - expect(orderInfo.orderHash).to.be.equal(expectedOrderHash); - expect(orderInfo.orderTakerAssetFilledAmount).to.be.bignumber.equal(expectedTakerAssetFilledAmount); - expect(orderInfo.orderStatus).to.equal(expectedOrderStatus); - }); - }); -}); -// tslint:disable:max-file-line-count -// tslint:enable:no-unnecessary-type-assertion diff --git a/contracts/core/test/exchange/dispatcher.ts b/contracts/core/test/exchange/dispatcher.ts deleted file mode 100644 index 5b1ac1e20..000000000 --- a/contracts/core/test/exchange/dispatcher.ts +++ /dev/null @@ -1,284 +0,0 @@ -import { - chaiSetup, - constants, - expectTransactionFailedAsync, - LogDecoder, - provider, - txDefaults, - web3Wrapper, -} from '@0x/contracts-test-utils'; -import { DummyERC20TokenContract } from '@0x/contracts-tokens'; -import { BlockchainLifecycle } from '@0x/dev-utils'; -import { assetDataUtils } from '@0x/order-utils'; -import { AssetProxyId, RevertReason } from '@0x/types'; -import { BigNumber } from '@0x/utils'; -import * as chai from 'chai'; -import { LogWithDecodedArgs } from 'ethereum-types'; -import * as _ from 'lodash'; - -import { ERC20ProxyContract } from '../../generated-wrappers/erc20_proxy'; -import { ERC721ProxyContract } from '../../generated-wrappers/erc721_proxy'; -import { - TestAssetProxyDispatcherAssetProxyRegisteredEventArgs, - TestAssetProxyDispatcherContract, -} from '../../generated-wrappers/test_asset_proxy_dispatcher'; -import { artifacts } from '../../src/artifacts'; -import { ERC20Wrapper } from '../utils/erc20_wrapper'; -import { ERC721Wrapper } from '../utils/erc721_wrapper'; - -chaiSetup.configure(); -const expect = chai.expect; -const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); -// tslint:disable:no-unnecessary-type-assertion -describe('AssetProxyDispatcher', () => { - let owner: string; - let notOwner: string; - let makerAddress: string; - let takerAddress: string; - - let zrxToken: DummyERC20TokenContract; - let erc20Proxy: ERC20ProxyContract; - let erc721Proxy: ERC721ProxyContract; - let assetProxyDispatcher: TestAssetProxyDispatcherContract; - - let erc20Wrapper: ERC20Wrapper; - let erc721Wrapper: ERC721Wrapper; - - before(async () => { - await blockchainLifecycle.startAsync(); - }); - after(async () => { - await blockchainLifecycle.revertAsync(); - }); - before(async () => { - // Setup accounts & addresses - const accounts = await web3Wrapper.getAvailableAddressesAsync(); - const usedAddresses = ([owner, notOwner, makerAddress, takerAddress] = _.slice(accounts, 0, 4)); - - erc20Wrapper = new ERC20Wrapper(provider, usedAddresses, owner); - erc721Wrapper = new ERC721Wrapper(provider, usedAddresses, owner); - - const numDummyErc20ToDeploy = 1; - [zrxToken] = await erc20Wrapper.deployDummyTokensAsync(numDummyErc20ToDeploy, constants.DUMMY_TOKEN_DECIMALS); - erc20Proxy = await erc20Wrapper.deployProxyAsync(); - await erc20Wrapper.setBalancesAndAllowancesAsync(); - - erc721Proxy = await erc721Wrapper.deployProxyAsync(); - - assetProxyDispatcher = await TestAssetProxyDispatcherContract.deployFrom0xArtifactAsync( - artifacts.TestAssetProxyDispatcher, - provider, - txDefaults, - ); - await web3Wrapper.awaitTransactionSuccessAsync( - await erc20Proxy.addAuthorizedAddress.sendTransactionAsync(assetProxyDispatcher.address, { - from: owner, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - await web3Wrapper.awaitTransactionSuccessAsync( - await erc721Proxy.addAuthorizedAddress.sendTransactionAsync(assetProxyDispatcher.address, { - from: owner, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - }); - beforeEach(async () => { - await blockchainLifecycle.startAsync(); - }); - afterEach(async () => { - await blockchainLifecycle.revertAsync(); - }); - describe('registerAssetProxy', () => { - it('should record proxy upon registration', async () => { - await web3Wrapper.awaitTransactionSuccessAsync( - await assetProxyDispatcher.registerAssetProxy.sendTransactionAsync(erc20Proxy.address, { from: owner }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - const proxyAddress = await assetProxyDispatcher.getAssetProxy.callAsync(AssetProxyId.ERC20); - expect(proxyAddress).to.be.equal(erc20Proxy.address); - }); - - it('should be able to record multiple proxies', async () => { - // Record first proxy - await web3Wrapper.awaitTransactionSuccessAsync( - await assetProxyDispatcher.registerAssetProxy.sendTransactionAsync(erc20Proxy.address, { from: owner }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - let proxyAddress = await assetProxyDispatcher.getAssetProxy.callAsync(AssetProxyId.ERC20); - expect(proxyAddress).to.be.equal(erc20Proxy.address); - // Record another proxy - await web3Wrapper.awaitTransactionSuccessAsync( - await assetProxyDispatcher.registerAssetProxy.sendTransactionAsync(erc721Proxy.address, { - from: owner, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - proxyAddress = await assetProxyDispatcher.getAssetProxy.callAsync(AssetProxyId.ERC721); - expect(proxyAddress).to.be.equal(erc721Proxy.address); - }); - - it('should throw if a proxy with the same id is already registered', async () => { - // Initial registration - await web3Wrapper.awaitTransactionSuccessAsync( - await assetProxyDispatcher.registerAssetProxy.sendTransactionAsync(erc20Proxy.address, { from: owner }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - const proxyAddress = await assetProxyDispatcher.getAssetProxy.callAsync(AssetProxyId.ERC20); - expect(proxyAddress).to.be.equal(erc20Proxy.address); - // Deploy a new version of the ERC20 Transfer Proxy contract - const newErc20TransferProxy = await ERC20ProxyContract.deployFrom0xArtifactAsync( - artifacts.ERC20Proxy, - provider, - txDefaults, - ); - // Register new ERC20 Transfer Proxy contract - return expectTransactionFailedAsync( - assetProxyDispatcher.registerAssetProxy.sendTransactionAsync(newErc20TransferProxy.address, { - from: owner, - }), - RevertReason.AssetProxyAlreadyExists, - ); - }); - - it('should throw if requesting address is not owner', async () => { - return expectTransactionFailedAsync( - assetProxyDispatcher.registerAssetProxy.sendTransactionAsync(erc20Proxy.address, { from: notOwner }), - RevertReason.OnlyContractOwner, - ); - }); - - it('should log an event with correct arguments when an asset proxy is registered', async () => { - const logDecoder = new LogDecoder(web3Wrapper, artifacts); - const txReceipt = await logDecoder.getTxWithDecodedLogsAsync( - await assetProxyDispatcher.registerAssetProxy.sendTransactionAsync(erc20Proxy.address, { from: owner }), - ); - const logs = txReceipt.logs; - const log = logs[0] as LogWithDecodedArgs<TestAssetProxyDispatcherAssetProxyRegisteredEventArgs>; - expect(log.args.id).to.equal(AssetProxyId.ERC20); - expect(log.args.assetProxy).to.equal(erc20Proxy.address); - }); - }); - - describe('getAssetProxy', () => { - it('should return correct address of registered proxy', async () => { - await web3Wrapper.awaitTransactionSuccessAsync( - await assetProxyDispatcher.registerAssetProxy.sendTransactionAsync(erc20Proxy.address, { from: owner }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - const proxyAddress = await assetProxyDispatcher.getAssetProxy.callAsync(AssetProxyId.ERC20); - expect(proxyAddress).to.be.equal(erc20Proxy.address); - }); - - it('should return NULL address if requesting non-existent proxy', async () => { - const proxyAddress = await assetProxyDispatcher.getAssetProxy.callAsync(AssetProxyId.ERC20); - expect(proxyAddress).to.be.equal(constants.NULL_ADDRESS); - }); - }); - - describe('dispatchTransferFrom', () => { - it('should dispatch transfer to registered proxy', async () => { - // Register ERC20 proxy - await web3Wrapper.awaitTransactionSuccessAsync( - await assetProxyDispatcher.registerAssetProxy.sendTransactionAsync(erc20Proxy.address, { from: owner }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - // Construct metadata for ERC20 proxy - const encodedAssetData = assetDataUtils.encodeERC20AssetData(zrxToken.address); - - // Perform a transfer from makerAddress to takerAddress - const erc20Balances = await erc20Wrapper.getBalancesAsync(); - const amount = new BigNumber(10); - await web3Wrapper.awaitTransactionSuccessAsync( - await assetProxyDispatcher.publicDispatchTransferFrom.sendTransactionAsync( - encodedAssetData, - makerAddress, - takerAddress, - amount, - { from: owner }, - ), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - // Verify transfer was successful - const newBalances = await erc20Wrapper.getBalancesAsync(); - expect(newBalances[makerAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[makerAddress][zrxToken.address].minus(amount), - ); - expect(newBalances[takerAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[takerAddress][zrxToken.address].add(amount), - ); - }); - - it('should not dispatch a transfer if amount == 0', async () => { - // Register ERC20 proxy - await web3Wrapper.awaitTransactionSuccessAsync( - await assetProxyDispatcher.registerAssetProxy.sendTransactionAsync(erc20Proxy.address, { from: owner }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - // Construct metadata for ERC20 proxy - const encodedAssetData = assetDataUtils.encodeERC20AssetData(zrxToken.address); - - // Perform a transfer from makerAddress to takerAddress - const erc20Balances = await erc20Wrapper.getBalancesAsync(); - const amount = constants.ZERO_AMOUNT; - const txReceipt = await web3Wrapper.awaitTransactionSuccessAsync( - await assetProxyDispatcher.publicDispatchTransferFrom.sendTransactionAsync( - encodedAssetData, - makerAddress, - takerAddress, - amount, - { from: owner }, - ), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - expect(txReceipt.logs.length).to.be.equal(0); - const newBalances = await erc20Wrapper.getBalancesAsync(); - expect(newBalances).to.deep.equal(erc20Balances); - }); - - it('should not dispatch a transfer if from == to', async () => { - // Register ERC20 proxy - await web3Wrapper.awaitTransactionSuccessAsync( - await assetProxyDispatcher.registerAssetProxy.sendTransactionAsync(erc20Proxy.address, { from: owner }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - // Construct metadata for ERC20 proxy - const encodedAssetData = assetDataUtils.encodeERC20AssetData(zrxToken.address); - - // Perform a transfer from makerAddress to takerAddress - const erc20Balances = await erc20Wrapper.getBalancesAsync(); - const amount = new BigNumber(10); - const txReceipt = await web3Wrapper.awaitTransactionSuccessAsync( - await assetProxyDispatcher.publicDispatchTransferFrom.sendTransactionAsync( - encodedAssetData, - makerAddress, - makerAddress, - amount, - { from: owner }, - ), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - expect(txReceipt.logs.length).to.be.equal(0); - const newBalances = await erc20Wrapper.getBalancesAsync(); - expect(newBalances).to.deep.equal(erc20Balances); - }); - - it('should throw if dispatching to unregistered proxy', async () => { - // Construct metadata for ERC20 proxy - const encodedAssetData = assetDataUtils.encodeERC20AssetData(zrxToken.address); - // Perform a transfer from makerAddress to takerAddress - const amount = new BigNumber(10); - return expectTransactionFailedAsync( - assetProxyDispatcher.publicDispatchTransferFrom.sendTransactionAsync( - encodedAssetData, - makerAddress, - takerAddress, - amount, - { from: owner }, - ), - RevertReason.AssetProxyDoesNotExist, - ); - }); - }); -}); -// tslint:enable:no-unnecessary-type-assertion diff --git a/contracts/core/test/exchange/fill_order.ts b/contracts/core/test/exchange/fill_order.ts deleted file mode 100644 index 2bdbe4855..000000000 --- a/contracts/core/test/exchange/fill_order.ts +++ /dev/null @@ -1,314 +0,0 @@ -import { - AllowanceAmountScenario, - AssetDataScenario, - BalanceAmountScenario, - chaiSetup, - ExpirationTimeSecondsScenario, - FeeRecipientAddressScenario, - FillScenario, - OrderAssetAmountScenario, - provider, - TakerAssetFillAmountScenario, - TakerScenario, - txDefaults, - web3Wrapper, -} from '@0x/contracts-test-utils'; -import { BlockchainLifecycle } from '@0x/dev-utils'; -import * as _ from 'lodash'; - -import { - FillOrderCombinatorialUtils, - fillOrderCombinatorialUtilsFactoryAsync, -} from '../utils/fill_order_combinatorial_utils'; - -chaiSetup.configure(); -const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); - -const defaultFillScenario = { - orderScenario: { - takerScenario: TakerScenario.Unspecified, - feeRecipientScenario: FeeRecipientAddressScenario.EthUserAddress, - makerAssetAmountScenario: OrderAssetAmountScenario.Large, - takerAssetAmountScenario: OrderAssetAmountScenario.Large, - makerFeeScenario: OrderAssetAmountScenario.Large, - takerFeeScenario: OrderAssetAmountScenario.Large, - expirationTimeSecondsScenario: ExpirationTimeSecondsScenario.InFuture, - makerAssetDataScenario: AssetDataScenario.ERC20NonZRXEighteenDecimals, - takerAssetDataScenario: AssetDataScenario.ERC20NonZRXEighteenDecimals, - }, - takerAssetFillAmountScenario: TakerAssetFillAmountScenario.LessThanRemainingFillableTakerAssetAmount, - makerStateScenario: { - traderAssetBalance: BalanceAmountScenario.Higher, - traderAssetAllowance: AllowanceAmountScenario.Higher, - zrxFeeBalance: BalanceAmountScenario.Higher, - zrxFeeAllowance: AllowanceAmountScenario.Higher, - }, - takerStateScenario: { - traderAssetBalance: BalanceAmountScenario.Higher, - traderAssetAllowance: AllowanceAmountScenario.Higher, - zrxFeeBalance: BalanceAmountScenario.Higher, - zrxFeeAllowance: AllowanceAmountScenario.Higher, - }, -}; - -describe('FillOrder Tests', () => { - let fillOrderCombinatorialUtils: FillOrderCombinatorialUtils; - - before(async () => { - await blockchainLifecycle.startAsync(); - fillOrderCombinatorialUtils = await fillOrderCombinatorialUtilsFactoryAsync(web3Wrapper, txDefaults); - }); - after(async () => { - await blockchainLifecycle.revertAsync(); - }); - beforeEach(async () => { - await blockchainLifecycle.startAsync(); - }); - afterEach(async () => { - await blockchainLifecycle.revertAsync(); - }); - describe('fillOrder', () => { - const test = (fillScenarios: FillScenario[]) => { - _.forEach(fillScenarios, fillScenario => { - const description = `Combinatorial OrderFill: ${JSON.stringify(fillScenario)}`; - it(description, async () => { - await fillOrderCombinatorialUtils.testFillOrderScenarioAsync(provider, fillScenario); - }); - }); - }; - - const allFillScenarios = FillOrderCombinatorialUtils.generateFillOrderCombinations(); - describe('Combinatorially generated fills orders', () => test(allFillScenarios)); - - it('should transfer the correct amounts when makerAssetAmount === takerAssetAmount', async () => { - const fillScenario = { - ...defaultFillScenario, - }; - await fillOrderCombinatorialUtils.testFillOrderScenarioAsync(provider, fillScenario); - }); - it('should transfer the correct amounts when makerAssetAmount > takerAssetAmount', async () => { - const fillScenario = { - ...defaultFillScenario, - orderScenario: { - ...defaultFillScenario.orderScenario, - takerAssetAmountScenario: OrderAssetAmountScenario.Small, - }, - }; - await fillOrderCombinatorialUtils.testFillOrderScenarioAsync(provider, fillScenario); - }); - it('should transfer the correct amounts when makerAssetAmount < takerAssetAmount', async () => { - const fillScenario = { - ...defaultFillScenario, - orderScenario: { - ...defaultFillScenario.orderScenario, - makerAssetAmountScenario: OrderAssetAmountScenario.Small, - }, - }; - await fillOrderCombinatorialUtils.testFillOrderScenarioAsync(provider, fillScenario); - }); - it('should transfer the correct amounts when makerAssetAmount < takerAssetAmount with zero decimals', async () => { - const fillScenario = { - ...defaultFillScenario, - orderScenario: { - ...defaultFillScenario.orderScenario, - makerAssetAmountScenario: OrderAssetAmountScenario.Small, - makerAssetDataScenario: AssetDataScenario.ERC20ZeroDecimals, - }, - }; - await fillOrderCombinatorialUtils.testFillOrderScenarioAsync(provider, fillScenario); - }); - it('should transfer the correct amounts when taker is specified and order is claimed by taker', async () => { - const fillScenario = { - ...defaultFillScenario, - orderScenario: { - ...defaultFillScenario.orderScenario, - takerScenario: TakerScenario.CorrectlySpecified, - }, - }; - await fillOrderCombinatorialUtils.testFillOrderScenarioAsync(provider, fillScenario); - }); - it('should fill remaining value if takerAssetFillAmount > remaining takerAssetAmount', async () => { - const fillScenario = { - ...defaultFillScenario, - takerAssetFillAmountScenario: TakerAssetFillAmountScenario.GreaterThanRemainingFillableTakerAssetAmount, - }; - await fillOrderCombinatorialUtils.testFillOrderScenarioAsync(provider, fillScenario); - }); - it('should throw when taker is specified and order is claimed by other', async () => { - const fillScenario = { - ...defaultFillScenario, - orderScenario: { - ...defaultFillScenario.orderScenario, - takerScenario: TakerScenario.IncorrectlySpecified, - }, - }; - await fillOrderCombinatorialUtils.testFillOrderScenarioAsync(provider, fillScenario); - }); - - it('should throw if makerAssetAmount is 0', async () => { - const fillScenario = { - ...defaultFillScenario, - orderScenario: { - ...defaultFillScenario.orderScenario, - makerAssetAmountScenario: OrderAssetAmountScenario.Zero, - }, - takerAssetFillAmountScenario: TakerAssetFillAmountScenario.GreaterThanRemainingFillableTakerAssetAmount, - }; - await fillOrderCombinatorialUtils.testFillOrderScenarioAsync(provider, fillScenario); - }); - - it('should throw if takerAssetAmount is 0', async () => { - const fillScenario = { - ...defaultFillScenario, - orderScenario: { - ...defaultFillScenario.orderScenario, - takerAssetAmountScenario: OrderAssetAmountScenario.Zero, - }, - takerAssetFillAmountScenario: TakerAssetFillAmountScenario.GreaterThanRemainingFillableTakerAssetAmount, - }; - await fillOrderCombinatorialUtils.testFillOrderScenarioAsync(provider, fillScenario); - }); - - it('should throw if takerAssetFillAmount is 0', async () => { - const fillScenario = { - ...defaultFillScenario, - takerAssetFillAmountScenario: TakerAssetFillAmountScenario.Zero, - }; - await fillOrderCombinatorialUtils.testFillOrderScenarioAsync(provider, fillScenario); - }); - - it('should throw if an order is expired', async () => { - const fillScenario = { - ...defaultFillScenario, - orderScenario: { - ...defaultFillScenario.orderScenario, - expirationTimeSecondsScenario: ExpirationTimeSecondsScenario.InPast, - }, - }; - await fillOrderCombinatorialUtils.testFillOrderScenarioAsync(provider, fillScenario); - }); - - it('should throw if maker erc20Balances are too low to fill order', async () => { - const fillScenario = { - ...defaultFillScenario, - makerStateScenario: { - ...defaultFillScenario.makerStateScenario, - traderAssetBalance: BalanceAmountScenario.TooLow, - }, - }; - await fillOrderCombinatorialUtils.testFillOrderScenarioAsync(provider, fillScenario); - }); - - it('should throw if taker erc20Balances are too low to fill order', async () => { - const fillScenario = { - ...defaultFillScenario, - takerStateScenario: { - ...defaultFillScenario.makerStateScenario, - traderAssetBalance: BalanceAmountScenario.TooLow, - }, - }; - await fillOrderCombinatorialUtils.testFillOrderScenarioAsync(provider, fillScenario); - }); - - it('should throw if maker allowances are too low to fill order', async () => { - const fillScenario = { - ...defaultFillScenario, - makerStateScenario: { - ...defaultFillScenario.makerStateScenario, - traderAssetAllowance: AllowanceAmountScenario.TooLow, - }, - }; - await fillOrderCombinatorialUtils.testFillOrderScenarioAsync(provider, fillScenario); - }); - - it('should throw if taker allowances are too low to fill order', async () => { - const fillScenario = { - ...defaultFillScenario, - takerStateScenario: { - ...defaultFillScenario.makerStateScenario, - traderAssetAllowance: AllowanceAmountScenario.TooLow, - }, - }; - await fillOrderCombinatorialUtils.testFillOrderScenarioAsync(provider, fillScenario); - }); - }); - - describe('Testing exchange of ERC721 Tokens', () => { - it('should successfully exchange a single token between the maker and taker (via fillOrder)', async () => { - const fillScenario = { - ...defaultFillScenario, - orderScenario: { - ...defaultFillScenario.orderScenario, - makerAssetDataScenario: AssetDataScenario.ERC721, - takerAssetDataScenario: AssetDataScenario.ERC721, - }, - takerAssetFillAmountScenario: TakerAssetFillAmountScenario.ExactlyRemainingFillableTakerAssetAmount, - }; - await fillOrderCombinatorialUtils.testFillOrderScenarioAsync(provider, fillScenario); - }); - - it('should successfully fill order when makerAsset is ERC721 and takerAsset is ERC20', async () => { - const fillScenario = { - ...defaultFillScenario, - orderScenario: { - ...defaultFillScenario.orderScenario, - makerAssetDataScenario: AssetDataScenario.ERC721, - takerAssetDataScenario: AssetDataScenario.ERC20NonZRXEighteenDecimals, - }, - takerAssetFillAmountScenario: TakerAssetFillAmountScenario.ExactlyRemainingFillableTakerAssetAmount, - }; - await fillOrderCombinatorialUtils.testFillOrderScenarioAsync(provider, fillScenario, true); - }); - - it('should successfully fill order when makerAsset is ERC20 and takerAsset is ERC721', async () => { - const fillScenario = { - ...defaultFillScenario, - orderScenario: { - ...defaultFillScenario.orderScenario, - makerAssetDataScenario: AssetDataScenario.ERC20NonZRXEighteenDecimals, - takerAssetDataScenario: AssetDataScenario.ERC721, - }, - takerAssetFillAmountScenario: TakerAssetFillAmountScenario.ExactlyRemainingFillableTakerAssetAmount, - }; - await fillOrderCombinatorialUtils.testFillOrderScenarioAsync(provider, fillScenario); - }); - - it('should successfully fill order when makerAsset is ERC721 and approveAll is set for it', async () => { - const fillScenario = { - ...defaultFillScenario, - orderScenario: { - ...defaultFillScenario.orderScenario, - makerAssetDataScenario: AssetDataScenario.ERC721, - takerAssetDataScenario: AssetDataScenario.ERC20NonZRXEighteenDecimals, - }, - takerAssetFillAmountScenario: TakerAssetFillAmountScenario.ExactlyRemainingFillableTakerAssetAmount, - makerStateScenario: { - ...defaultFillScenario.makerStateScenario, - traderAssetAllowance: AllowanceAmountScenario.Unlimited, - }, - }; - await fillOrderCombinatorialUtils.testFillOrderScenarioAsync(provider, fillScenario); - }); - - it('should successfully fill order when makerAsset and takerAsset are ERC721 and approveAll is set for them', async () => { - const fillScenario = { - ...defaultFillScenario, - orderScenario: { - ...defaultFillScenario.orderScenario, - makerAssetDataScenario: AssetDataScenario.ERC721, - takerAssetDataScenario: AssetDataScenario.ERC721, - }, - takerAssetFillAmountScenario: TakerAssetFillAmountScenario.ExactlyRemainingFillableTakerAssetAmount, - makerStateScenario: { - ...defaultFillScenario.makerStateScenario, - traderAssetAllowance: AllowanceAmountScenario.Unlimited, - }, - takerStateScenario: { - ...defaultFillScenario.takerStateScenario, - traderAssetAllowance: AllowanceAmountScenario.Unlimited, - }, - }; - await fillOrderCombinatorialUtils.testFillOrderScenarioAsync(provider, fillScenario); - }); - }); -}); diff --git a/contracts/core/test/exchange/internal.ts b/contracts/core/test/exchange/internal.ts deleted file mode 100644 index 972f5efb6..000000000 --- a/contracts/core/test/exchange/internal.ts +++ /dev/null @@ -1,472 +0,0 @@ -import { - bytes32Values, - chaiSetup, - constants, - FillResults, - getRevertReasonOrErrorMessageForSendTransactionAsync, - provider, - testCombinatoriallyWithReferenceFuncAsync, - txDefaults, - uint256Values, - web3Wrapper, -} from '@0x/contracts-test-utils'; -import { BlockchainLifecycle } from '@0x/dev-utils'; -import { Order, RevertReason, SignedOrder } from '@0x/types'; -import { BigNumber } from '@0x/utils'; -import * as chai from 'chai'; -import * as _ from 'lodash'; - -import { TestExchangeInternalsContract } from '../../generated-wrappers/test_exchange_internals'; -import { artifacts } from '../../src/artifacts'; - -chaiSetup.configure(); -const expect = chai.expect; - -const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); - -const MAX_UINT256 = new BigNumber(2).pow(256).minus(1); - -const emptyOrder: Order = { - senderAddress: constants.NULL_ADDRESS, - makerAddress: constants.NULL_ADDRESS, - takerAddress: constants.NULL_ADDRESS, - makerFee: new BigNumber(0), - takerFee: new BigNumber(0), - makerAssetAmount: new BigNumber(0), - takerAssetAmount: new BigNumber(0), - makerAssetData: '0x', - takerAssetData: '0x', - salt: new BigNumber(0), - exchangeAddress: constants.NULL_ADDRESS, - feeRecipientAddress: constants.NULL_ADDRESS, - expirationTimeSeconds: new BigNumber(0), -}; - -const emptySignedOrder: SignedOrder = { - ...emptyOrder, - signature: '', -}; - -const overflowErrorForCall = new Error(RevertReason.Uint256Overflow); - -describe('Exchange core internal functions', () => { - let testExchange: TestExchangeInternalsContract; - let overflowErrorForSendTransaction: Error | undefined; - let divisionByZeroErrorForCall: Error | undefined; - let roundingErrorForCall: Error | undefined; - - before(async () => { - await blockchainLifecycle.startAsync(); - }); - after(async () => { - await blockchainLifecycle.revertAsync(); - }); - before(async () => { - testExchange = await TestExchangeInternalsContract.deployFrom0xArtifactAsync( - artifacts.TestExchangeInternals, - provider, - txDefaults, - ); - overflowErrorForSendTransaction = new Error( - await getRevertReasonOrErrorMessageForSendTransactionAsync(RevertReason.Uint256Overflow), - ); - divisionByZeroErrorForCall = new Error(RevertReason.DivisionByZero); - roundingErrorForCall = new Error(RevertReason.RoundingError); - }); - // Note(albrow): Don't forget to add beforeEach and afterEach calls to reset - // the blockchain state for any tests which modify it! - - async function referenceIsRoundingErrorFloorAsync( - numerator: BigNumber, - denominator: BigNumber, - target: BigNumber, - ): Promise<boolean> { - if (denominator.eq(0)) { - throw divisionByZeroErrorForCall; - } - if (numerator.eq(0)) { - return false; - } - if (target.eq(0)) { - return false; - } - const product = numerator.mul(target); - const remainder = product.mod(denominator); - const remainderTimes1000 = remainder.mul('1000'); - const isError = remainderTimes1000.gte(product); - if (product.greaterThan(MAX_UINT256)) { - throw overflowErrorForCall; - } - if (remainderTimes1000.greaterThan(MAX_UINT256)) { - throw overflowErrorForCall; - } - return isError; - } - - async function referenceIsRoundingErrorCeilAsync( - numerator: BigNumber, - denominator: BigNumber, - target: BigNumber, - ): Promise<boolean> { - if (denominator.eq(0)) { - throw divisionByZeroErrorForCall; - } - if (numerator.eq(0)) { - return false; - } - if (target.eq(0)) { - return false; - } - const product = numerator.mul(target); - const remainder = product.mod(denominator); - const error = denominator.sub(remainder).mod(denominator); - const errorTimes1000 = error.mul('1000'); - const isError = errorTimes1000.gte(product); - if (product.greaterThan(MAX_UINT256)) { - throw overflowErrorForCall; - } - if (errorTimes1000.greaterThan(MAX_UINT256)) { - throw overflowErrorForCall; - } - return isError; - } - - async function referenceSafeGetPartialAmountFloorAsync( - numerator: BigNumber, - denominator: BigNumber, - target: BigNumber, - ): Promise<BigNumber> { - if (denominator.eq(0)) { - throw divisionByZeroErrorForCall; - } - const isRoundingError = await referenceIsRoundingErrorFloorAsync(numerator, denominator, target); - if (isRoundingError) { - throw roundingErrorForCall; - } - const product = numerator.mul(target); - if (product.greaterThan(MAX_UINT256)) { - throw overflowErrorForCall; - } - return product.dividedToIntegerBy(denominator); - } - - describe('addFillResults', async () => { - function makeFillResults(value: BigNumber): FillResults { - return { - makerAssetFilledAmount: value, - takerAssetFilledAmount: value, - makerFeePaid: value, - takerFeePaid: value, - }; - } - async function referenceAddFillResultsAsync( - totalValue: BigNumber, - singleValue: BigNumber, - ): Promise<FillResults> { - // Note(albrow): Here, each of totalFillResults and - // singleFillResults will consist of fields with the same values. - // This should be safe because none of the fields in a given - // FillResults are ever used together in a mathemetical operation. - // They are only used with the corresponding field from *the other* - // FillResults, which are different. - const totalFillResults = makeFillResults(totalValue); - const singleFillResults = makeFillResults(singleValue); - // HACK(albrow): _.mergeWith mutates the first argument! To - // workaround this we use _.cloneDeep. - return _.mergeWith( - _.cloneDeep(totalFillResults), - singleFillResults, - (totalVal: BigNumber, singleVal: BigNumber) => { - const newTotal = totalVal.add(singleVal); - if (newTotal.greaterThan(MAX_UINT256)) { - throw overflowErrorForCall; - } - return newTotal; - }, - ); - } - async function testAddFillResultsAsync(totalValue: BigNumber, singleValue: BigNumber): Promise<FillResults> { - const totalFillResults = makeFillResults(totalValue); - const singleFillResults = makeFillResults(singleValue); - return testExchange.publicAddFillResults.callAsync(totalFillResults, singleFillResults); - } - await testCombinatoriallyWithReferenceFuncAsync( - 'addFillResults', - referenceAddFillResultsAsync, - testAddFillResultsAsync, - [uint256Values, uint256Values], - ); - }); - - describe('calculateFillResults', async () => { - function makeOrder( - makerAssetAmount: BigNumber, - takerAssetAmount: BigNumber, - makerFee: BigNumber, - takerFee: BigNumber, - ): Order { - return { - ...emptyOrder, - makerAssetAmount, - takerAssetAmount, - makerFee, - takerFee, - }; - } - async function referenceCalculateFillResultsAsync( - orderTakerAssetAmount: BigNumber, - takerAssetFilledAmount: BigNumber, - otherAmount: BigNumber, - ): Promise<FillResults> { - // Note(albrow): Here we are re-using the same value (otherAmount) - // for order.makerAssetAmount, order.makerFee, and order.takerFee. - // This should be safe because they are never used with each other - // in any mathematical operation in either the reference TypeScript - // implementation or the Solidity implementation of - // calculateFillResults. - const makerAssetFilledAmount = await referenceSafeGetPartialAmountFloorAsync( - takerAssetFilledAmount, - orderTakerAssetAmount, - otherAmount, - ); - const order = makeOrder(otherAmount, orderTakerAssetAmount, otherAmount, otherAmount); - const orderMakerAssetAmount = order.makerAssetAmount; - return { - makerAssetFilledAmount, - takerAssetFilledAmount, - makerFeePaid: await referenceSafeGetPartialAmountFloorAsync( - makerAssetFilledAmount, - orderMakerAssetAmount, - otherAmount, - ), - takerFeePaid: await referenceSafeGetPartialAmountFloorAsync( - takerAssetFilledAmount, - orderTakerAssetAmount, - otherAmount, - ), - }; - } - async function testCalculateFillResultsAsync( - orderTakerAssetAmount: BigNumber, - takerAssetFilledAmount: BigNumber, - otherAmount: BigNumber, - ): Promise<FillResults> { - const order = makeOrder(otherAmount, orderTakerAssetAmount, otherAmount, otherAmount); - return testExchange.publicCalculateFillResults.callAsync(order, takerAssetFilledAmount); - } - await testCombinatoriallyWithReferenceFuncAsync( - 'calculateFillResults', - referenceCalculateFillResultsAsync, - testCalculateFillResultsAsync, - [uint256Values, uint256Values, uint256Values], - ); - }); - - describe('getPartialAmountFloor', async () => { - async function referenceGetPartialAmountFloorAsync( - numerator: BigNumber, - denominator: BigNumber, - target: BigNumber, - ): Promise<BigNumber> { - if (denominator.eq(0)) { - throw divisionByZeroErrorForCall; - } - const product = numerator.mul(target); - if (product.greaterThan(MAX_UINT256)) { - throw overflowErrorForCall; - } - return product.dividedToIntegerBy(denominator); - } - async function testGetPartialAmountFloorAsync( - numerator: BigNumber, - denominator: BigNumber, - target: BigNumber, - ): Promise<BigNumber> { - return testExchange.publicGetPartialAmountFloor.callAsync(numerator, denominator, target); - } - await testCombinatoriallyWithReferenceFuncAsync( - 'getPartialAmountFloor', - referenceGetPartialAmountFloorAsync, - testGetPartialAmountFloorAsync, - [uint256Values, uint256Values, uint256Values], - ); - }); - - describe('getPartialAmountCeil', async () => { - async function referenceGetPartialAmountCeilAsync( - numerator: BigNumber, - denominator: BigNumber, - target: BigNumber, - ): Promise<BigNumber> { - if (denominator.eq(0)) { - throw divisionByZeroErrorForCall; - } - const product = numerator.mul(target); - const offset = product.add(denominator.sub(1)); - if (offset.greaterThan(MAX_UINT256)) { - throw overflowErrorForCall; - } - const result = offset.dividedToIntegerBy(denominator); - if (product.mod(denominator).eq(0)) { - expect(result.mul(denominator)).to.be.bignumber.eq(product); - } else { - expect(result.mul(denominator)).to.be.bignumber.gt(product); - } - return result; - } - async function testGetPartialAmountCeilAsync( - numerator: BigNumber, - denominator: BigNumber, - target: BigNumber, - ): Promise<BigNumber> { - return testExchange.publicGetPartialAmountCeil.callAsync(numerator, denominator, target); - } - await testCombinatoriallyWithReferenceFuncAsync( - 'getPartialAmountCeil', - referenceGetPartialAmountCeilAsync, - testGetPartialAmountCeilAsync, - [uint256Values, uint256Values, uint256Values], - ); - }); - - describe('safeGetPartialAmountFloor', async () => { - async function testSafeGetPartialAmountFloorAsync( - numerator: BigNumber, - denominator: BigNumber, - target: BigNumber, - ): Promise<BigNumber> { - return testExchange.publicSafeGetPartialAmountFloor.callAsync(numerator, denominator, target); - } - await testCombinatoriallyWithReferenceFuncAsync( - 'safeGetPartialAmountFloor', - referenceSafeGetPartialAmountFloorAsync, - testSafeGetPartialAmountFloorAsync, - [uint256Values, uint256Values, uint256Values], - ); - }); - - describe('safeGetPartialAmountCeil', async () => { - async function referenceSafeGetPartialAmountCeilAsync( - numerator: BigNumber, - denominator: BigNumber, - target: BigNumber, - ): Promise<BigNumber> { - if (denominator.eq(0)) { - throw divisionByZeroErrorForCall; - } - const isRoundingError = await referenceIsRoundingErrorCeilAsync(numerator, denominator, target); - if (isRoundingError) { - throw roundingErrorForCall; - } - const product = numerator.mul(target); - const offset = product.add(denominator.sub(1)); - if (offset.greaterThan(MAX_UINT256)) { - throw overflowErrorForCall; - } - const result = offset.dividedToIntegerBy(denominator); - if (product.mod(denominator).eq(0)) { - expect(result.mul(denominator)).to.be.bignumber.eq(product); - } else { - expect(result.mul(denominator)).to.be.bignumber.gt(product); - } - return result; - } - async function testSafeGetPartialAmountCeilAsync( - numerator: BigNumber, - denominator: BigNumber, - target: BigNumber, - ): Promise<BigNumber> { - return testExchange.publicSafeGetPartialAmountCeil.callAsync(numerator, denominator, target); - } - await testCombinatoriallyWithReferenceFuncAsync( - 'safeGetPartialAmountCeil', - referenceSafeGetPartialAmountCeilAsync, - testSafeGetPartialAmountCeilAsync, - [uint256Values, uint256Values, uint256Values], - ); - }); - - describe('isRoundingErrorFloor', async () => { - async function testIsRoundingErrorFloorAsync( - numerator: BigNumber, - denominator: BigNumber, - target: BigNumber, - ): Promise<boolean> { - return testExchange.publicIsRoundingErrorFloor.callAsync(numerator, denominator, target); - } - await testCombinatoriallyWithReferenceFuncAsync( - 'isRoundingErrorFloor', - referenceIsRoundingErrorFloorAsync, - testIsRoundingErrorFloorAsync, - [uint256Values, uint256Values, uint256Values], - ); - }); - - describe('isRoundingErrorCeil', async () => { - async function testIsRoundingErrorCeilAsync( - numerator: BigNumber, - denominator: BigNumber, - target: BigNumber, - ): Promise<boolean> { - return testExchange.publicIsRoundingErrorCeil.callAsync(numerator, denominator, target); - } - await testCombinatoriallyWithReferenceFuncAsync( - 'isRoundingErrorCeil', - referenceIsRoundingErrorCeilAsync, - testIsRoundingErrorCeilAsync, - [uint256Values, uint256Values, uint256Values], - ); - }); - - describe('updateFilledState', async () => { - // Note(albrow): Since updateFilledState modifies the state by calling - // sendTransaction, we must reset the state after each test. - beforeEach(async () => { - await blockchainLifecycle.startAsync(); - }); - afterEach(async () => { - await blockchainLifecycle.revertAsync(); - }); - async function referenceUpdateFilledStateAsync( - takerAssetFilledAmount: BigNumber, - orderTakerAssetFilledAmount: BigNumber, - // tslint:disable-next-line:no-unused-variable - orderHash: string, - ): Promise<BigNumber> { - const totalFilledAmount = takerAssetFilledAmount.add(orderTakerAssetFilledAmount); - if (totalFilledAmount.greaterThan(MAX_UINT256)) { - throw overflowErrorForSendTransaction; - } - return totalFilledAmount; - } - async function testUpdateFilledStateAsync( - takerAssetFilledAmount: BigNumber, - orderTakerAssetFilledAmount: BigNumber, - orderHash: string, - ): Promise<BigNumber> { - const fillResults = { - makerAssetFilledAmount: new BigNumber(0), - takerAssetFilledAmount, - makerFeePaid: new BigNumber(0), - takerFeePaid: new BigNumber(0), - }; - await web3Wrapper.awaitTransactionSuccessAsync( - await testExchange.publicUpdateFilledState.sendTransactionAsync( - emptySignedOrder, - constants.NULL_ADDRESS, - orderHash, - orderTakerAssetFilledAmount, - fillResults, - ), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - return testExchange.filled.callAsync(orderHash); - } - await testCombinatoriallyWithReferenceFuncAsync( - 'updateFilledState', - referenceUpdateFilledStateAsync, - testUpdateFilledStateAsync, - [uint256Values, uint256Values, bytes32Values], - ); - }); -}); diff --git a/contracts/core/test/exchange/match_orders.ts b/contracts/core/test/exchange/match_orders.ts deleted file mode 100644 index 8e943c237..000000000 --- a/contracts/core/test/exchange/match_orders.ts +++ /dev/null @@ -1,1284 +0,0 @@ -import { - chaiSetup, - constants, - ERC20BalancesByOwner, - ERC721TokenIdsByOwner, - expectTransactionFailedAsync, - OrderFactory, - provider, - txDefaults, - web3Wrapper, -} from '@0x/contracts-test-utils'; -import { - artifacts as tokensArtifacts, - DummyERC20TokenContract, - DummyERC721TokenContract, - ReentrantERC20TokenContract, -} from '@0x/contracts-tokens'; -import { BlockchainLifecycle } from '@0x/dev-utils'; -import { assetDataUtils } from '@0x/order-utils'; -import { RevertReason } from '@0x/types'; -import { BigNumber } from '@0x/utils'; -import { Web3Wrapper } from '@0x/web3-wrapper'; -import * as chai from 'chai'; -import * as _ from 'lodash'; - -import { ERC20ProxyContract } from '../../generated-wrappers/erc20_proxy'; -import { ERC721ProxyContract } from '../../generated-wrappers/erc721_proxy'; -import { ExchangeContract } from '../../generated-wrappers/exchange'; -import { TestExchangeInternalsContract } from '../../generated-wrappers/test_exchange_internals'; -import { artifacts } from '../../src/artifacts'; -import { ERC20Wrapper } from '../utils/erc20_wrapper'; -import { ERC721Wrapper } from '../utils/erc721_wrapper'; -import { ExchangeWrapper } from '../utils/exchange_wrapper'; -import { MatchOrderTester } from '../utils/match_order_tester'; - -const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); -chaiSetup.configure(); -const expect = chai.expect; - -describe('matchOrders', () => { - let makerAddressLeft: string; - let makerAddressRight: string; - let owner: string; - let takerAddress: string; - let feeRecipientAddressLeft: string; - let feeRecipientAddressRight: string; - - let erc20TokenA: DummyERC20TokenContract; - let erc20TokenB: DummyERC20TokenContract; - let zrxToken: DummyERC20TokenContract; - let erc721Token: DummyERC721TokenContract; - let reentrantErc20Token: ReentrantERC20TokenContract; - let exchange: ExchangeContract; - let erc20Proxy: ERC20ProxyContract; - let erc721Proxy: ERC721ProxyContract; - - let erc20BalancesByOwner: ERC20BalancesByOwner; - let erc721TokenIdsByOwner: ERC721TokenIdsByOwner; - let exchangeWrapper: ExchangeWrapper; - let erc20Wrapper: ERC20Wrapper; - let erc721Wrapper: ERC721Wrapper; - let orderFactoryLeft: OrderFactory; - let orderFactoryRight: OrderFactory; - - let erc721LeftMakerAssetIds: BigNumber[]; - let erc721RightMakerAssetIds: BigNumber[]; - - let defaultERC20MakerAssetAddress: string; - let defaultERC20TakerAssetAddress: string; - let defaultERC721AssetAddress: string; - - let matchOrderTester: MatchOrderTester; - - let testExchange: TestExchangeInternalsContract; - - before(async () => { - await blockchainLifecycle.startAsync(); - }); - after(async () => { - await blockchainLifecycle.revertAsync(); - }); - before(async () => { - // Create accounts - const accounts = await web3Wrapper.getAvailableAddressesAsync(); - // Hack(albrow): Both Prettier and TSLint insert a trailing comma below - // but that is invalid syntax as of TypeScript version >= 2.8. We don't - // have the right fine-grained configuration options in TSLint, - // Prettier, or TypeScript, to reconcile this, so we will just have to - // wait for them to sort it out. We disable TSLint and Prettier for - // this part of the code for now. This occurs several times in this - // file. See https://github.com/prettier/prettier/issues/4624. - // prettier-ignore - const usedAddresses = ([ - owner, - makerAddressLeft, - makerAddressRight, - takerAddress, - feeRecipientAddressLeft, - // tslint:disable-next-line:trailing-comma - feeRecipientAddressRight - ] = _.slice(accounts, 0, 6)); - // Create wrappers - erc20Wrapper = new ERC20Wrapper(provider, usedAddresses, owner); - erc721Wrapper = new ERC721Wrapper(provider, usedAddresses, owner); - // Deploy ERC20 token & ERC20 proxy - const numDummyErc20ToDeploy = 3; - [erc20TokenA, erc20TokenB, zrxToken] = await erc20Wrapper.deployDummyTokensAsync( - numDummyErc20ToDeploy, - constants.DUMMY_TOKEN_DECIMALS, - ); - erc20Proxy = await erc20Wrapper.deployProxyAsync(); - await erc20Wrapper.setBalancesAndAllowancesAsync(); - // Deploy ERC721 token and proxy - [erc721Token] = await erc721Wrapper.deployDummyTokensAsync(); - erc721Proxy = await erc721Wrapper.deployProxyAsync(); - await erc721Wrapper.setBalancesAndAllowancesAsync(); - const erc721Balances = await erc721Wrapper.getBalancesAsync(); - erc721LeftMakerAssetIds = erc721Balances[makerAddressLeft][erc721Token.address]; - erc721RightMakerAssetIds = erc721Balances[makerAddressRight][erc721Token.address]; - // Depoy exchange - exchange = await ExchangeContract.deployFrom0xArtifactAsync( - artifacts.Exchange, - provider, - txDefaults, - assetDataUtils.encodeERC20AssetData(zrxToken.address), - ); - exchangeWrapper = new ExchangeWrapper(exchange, provider); - await exchangeWrapper.registerAssetProxyAsync(erc20Proxy.address, owner); - await exchangeWrapper.registerAssetProxyAsync(erc721Proxy.address, owner); - // Authorize ERC20 and ERC721 trades by exchange - await web3Wrapper.awaitTransactionSuccessAsync( - await erc20Proxy.addAuthorizedAddress.sendTransactionAsync(exchange.address, { - from: owner, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - await web3Wrapper.awaitTransactionSuccessAsync( - await erc721Proxy.addAuthorizedAddress.sendTransactionAsync(exchange.address, { - from: owner, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - - reentrantErc20Token = await ReentrantERC20TokenContract.deployFrom0xArtifactAsync( - tokensArtifacts.ReentrantERC20Token, - provider, - txDefaults, - exchange.address, - ); - - // Set default addresses - defaultERC20MakerAssetAddress = erc20TokenA.address; - defaultERC20TakerAssetAddress = erc20TokenB.address; - defaultERC721AssetAddress = erc721Token.address; - // Create default order parameters - const defaultOrderParamsLeft = { - ...constants.STATIC_ORDER_PARAMS, - makerAddress: makerAddressLeft, - exchangeAddress: exchange.address, - makerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress), - takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress), - feeRecipientAddress: feeRecipientAddressLeft, - }; - const defaultOrderParamsRight = { - ...constants.STATIC_ORDER_PARAMS, - makerAddress: makerAddressRight, - exchangeAddress: exchange.address, - makerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress), - takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress), - feeRecipientAddress: feeRecipientAddressRight, - }; - const privateKeyLeft = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddressLeft)]; - orderFactoryLeft = new OrderFactory(privateKeyLeft, defaultOrderParamsLeft); - const privateKeyRight = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddressRight)]; - orderFactoryRight = new OrderFactory(privateKeyRight, defaultOrderParamsRight); - // Set match order tester - matchOrderTester = new MatchOrderTester(exchangeWrapper, erc20Wrapper, erc721Wrapper, zrxToken.address); - testExchange = await TestExchangeInternalsContract.deployFrom0xArtifactAsync( - artifacts.TestExchangeInternals, - provider, - txDefaults, - ); - }); - beforeEach(async () => { - await blockchainLifecycle.startAsync(); - }); - afterEach(async () => { - await blockchainLifecycle.revertAsync(); - }); - describe('matchOrders', () => { - beforeEach(async () => { - erc20BalancesByOwner = await erc20Wrapper.getBalancesAsync(); - erc721TokenIdsByOwner = await erc721Wrapper.getBalancesAsync(); - }); - - it('Should transfer correct amounts when right order is fully filled and values pass isRoundingErrorFloor but fail isRoundingErrorCeil', async () => { - // Create orders to match - const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ - makerAddress: makerAddressLeft, - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(17), 0), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(98), 0), - feeRecipientAddress: feeRecipientAddressLeft, - }); - const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ - makerAddress: makerAddressRight, - makerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress), - takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress), - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(75), 0), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(13), 0), - feeRecipientAddress: feeRecipientAddressRight, - }); - // Assert is rounding error ceil & not rounding error floor - // These assertions are taken from MixinMatchOrders::calculateMatchedFillResults - // The rounding error is derived computating how much the left maker will sell. - const numerator = signedOrderLeft.makerAssetAmount; - const denominator = signedOrderLeft.takerAssetAmount; - const target = signedOrderRight.makerAssetAmount; - const isRoundingErrorCeil = await testExchange.publicIsRoundingErrorCeil.callAsync( - numerator, - denominator, - target, - ); - expect(isRoundingErrorCeil).to.be.true(); - const isRoundingErrorFloor = await testExchange.publicIsRoundingErrorFloor.callAsync( - numerator, - denominator, - target, - ); - expect(isRoundingErrorFloor).to.be.false(); - // Match signedOrderLeft with signedOrderRight - // Note that the left maker received a slightly better sell price. - // This is intentional; see note in MixinMatchOrders.calculateMatchedFillResults. - // Because the left maker received a slightly more favorable sell price, the fee - // paid by the left taker is slightly higher than that paid by the left maker. - // Fees can be thought of as a tax paid by the seller, derived from the sale price. - const expectedTransferAmounts = { - // Left Maker - amountSoldByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(13), 0), - amountBoughtByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(75), 0), - feePaidByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber('76.4705882352941176'), 16), // 76.47% - // Right Maker - amountSoldByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(75), 0), - amountBoughtByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(13), 0), - feePaidByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% - // Taker - amountReceivedByTaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(0), 0), - feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber('76.5306122448979591'), 16), // 76.53% - feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% - }; - await matchOrderTester.matchOrdersAndAssertEffectsAsync( - signedOrderLeft, - signedOrderRight, - takerAddress, - erc20BalancesByOwner, - erc721TokenIdsByOwner, - expectedTransferAmounts, - ); - }); - - it('Should transfer correct amounts when left order is fully filled and values pass isRoundingErrorCeil but fail isRoundingErrorFloor', async () => { - // Create orders to match - const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ - makerAddress: makerAddressLeft, - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(15), 0), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(90), 0), - feeRecipientAddress: feeRecipientAddressLeft, - }); - const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ - makerAddress: makerAddressRight, - makerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress), - takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress), - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(97), 0), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(14), 0), - feeRecipientAddress: feeRecipientAddressRight, - }); - // Assert is rounding error floor & not rounding error ceil - // These assertions are taken from MixinMatchOrders::calculateMatchedFillResults - // The rounding error is derived computating how much the right maker will buy. - const numerator = signedOrderRight.takerAssetAmount; - const denominator = signedOrderRight.makerAssetAmount; - const target = signedOrderLeft.takerAssetAmount; - const isRoundingErrorFloor = await testExchange.publicIsRoundingErrorFloor.callAsync( - numerator, - denominator, - target, - ); - expect(isRoundingErrorFloor).to.be.true(); - const isRoundingErrorCeil = await testExchange.publicIsRoundingErrorCeil.callAsync( - numerator, - denominator, - target, - ); - expect(isRoundingErrorCeil).to.be.false(); - // Match signedOrderLeft with signedOrderRight - // Note that the right maker received a slightly better purchase price. - // This is intentional; see note in MixinMatchOrders.calculateMatchedFillResults. - // Because the right maker received a slightly more favorable buy price, the fee - // paid by the right taker is slightly higher than that paid by the right maker. - // Fees can be thought of as a tax paid by the seller, derived from the sale price. - const expectedTransferAmounts = { - // Left Maker - amountSoldByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(15), 0), - amountBoughtByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(90), 0), - feePaidByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% - // Right Maker - amountSoldByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(90), 0), - amountBoughtByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(13), 0), - feePaidByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber('92.7835051546391752'), 16), // 92.78% - // Taker - amountReceivedByTaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 0), - feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% - feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber('92.8571428571428571'), 16), // 92.85% - }; - await matchOrderTester.matchOrdersAndAssertEffectsAsync( - signedOrderLeft, - signedOrderRight, - takerAddress, - erc20BalancesByOwner, - erc721TokenIdsByOwner, - expectedTransferAmounts, - ); - }); - - it('Should give right maker a better buy price when rounding', async () => { - // Create orders to match - const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ - makerAddress: makerAddressLeft, - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(16), 0), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(22), 0), - feeRecipientAddress: feeRecipientAddressLeft, - }); - const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ - makerAddress: makerAddressRight, - makerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress), - takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress), - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(83), 0), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(49), 0), - feeRecipientAddress: feeRecipientAddressRight, - }); - // Note: - // The correct price buy price for the right maker would yield (49/83) * 22 = 12.988 units - // of the left maker asset. This gets rounded up to 13, giving the right maker a better price. - // Note: - // The maker/taker fee percentage paid on the right order differs because - // they received different sale prices. The right maker pays a - // fee slightly lower than the right taker. - const expectedTransferAmounts = { - // Left Maker - amountSoldByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(16), 0), - amountBoughtByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(22), 0), - feePaidByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% - // Right Maker - amountSoldByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(22), 0), - amountBoughtByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(13), 0), - feePaidByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber('26.5060240963855421'), 16), // 26.506% - // Taker - amountReceivedByTaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(3), 0), - feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% - feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber('26.5306122448979591'), 16), // 26.531% - }; - // Match signedOrderLeft with signedOrderRight - await matchOrderTester.matchOrdersAndAssertEffectsAsync( - signedOrderLeft, - signedOrderRight, - takerAddress, - erc20BalancesByOwner, - erc721TokenIdsByOwner, - expectedTransferAmounts, - ); - }); - - it('Should give left maker a better sell price when rounding', async () => { - // Create orders to match - const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ - makerAddress: makerAddressLeft, - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(12), 0), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(97), 0), - feeRecipientAddress: feeRecipientAddressLeft, - }); - const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ - makerAddress: makerAddressRight, - makerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress), - takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress), - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(89), 0), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(1), 0), - feeRecipientAddress: feeRecipientAddressRight, - }); - // Note: - // The maker/taker fee percentage paid on the left order differs because - // they received different sale prices. The left maker pays a fee - // slightly lower than the left taker. - const expectedTransferAmounts = { - // Left Maker - amountSoldByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(11), 0), - amountBoughtByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(89), 0), - feePaidByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber('91.6666666666666666'), 16), // 91.6% - // Right Maker - amountSoldByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(89), 0), - amountBoughtByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(1), 0), - feePaidByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% - // Taker - amountReceivedByTaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 0), - feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber('91.7525773195876288'), 16), // 91.75% - feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% - }; - // Match signedOrderLeft with signedOrderRight - await matchOrderTester.matchOrdersAndAssertEffectsAsync( - signedOrderLeft, - signedOrderRight, - takerAddress, - erc20BalancesByOwner, - erc721TokenIdsByOwner, - expectedTransferAmounts, - ); - }); - - it('Should give right maker and right taker a favorable fee price when rounding', async () => { - // Create orders to match - const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ - makerAddress: makerAddressLeft, - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(16), 0), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(22), 0), - feeRecipientAddress: feeRecipientAddressLeft, - }); - const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ - makerAddress: makerAddressRight, - makerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress), - takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress), - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(83), 0), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(49), 0), - feeRecipientAddress: feeRecipientAddressRight, - makerFee: Web3Wrapper.toBaseUnitAmount(new BigNumber(10000), 0), - takerFee: Web3Wrapper.toBaseUnitAmount(new BigNumber(10000), 0), - }); - // Note: - // The maker/taker fee percentage paid on the right order differs because - // they received different sale prices. The right maker pays a - // fee slightly lower than the right taker. - const expectedTransferAmounts = { - // Left Maker - amountSoldByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(16), 0), - amountBoughtByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(22), 0), - feePaidByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% - // Right Maker - amountSoldByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(22), 0), - amountBoughtByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(13), 0), - feePaidByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(2650), 0), // 2650.6 rounded down tro 2650 - // Taker - amountReceivedByTaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(3), 0), - feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% - feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(2653), 0), // 2653.1 rounded down to 2653 - }; - // Match signedOrderLeft with signedOrderRight - await matchOrderTester.matchOrdersAndAssertEffectsAsync( - signedOrderLeft, - signedOrderRight, - takerAddress, - erc20BalancesByOwner, - erc721TokenIdsByOwner, - expectedTransferAmounts, - ); - }); - - it('Should give left maker and left taker a favorable fee price when rounding', async () => { - // Create orders to match - const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ - makerAddress: makerAddressLeft, - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(12), 0), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(97), 0), - feeRecipientAddress: feeRecipientAddressLeft, - makerFee: Web3Wrapper.toBaseUnitAmount(new BigNumber(10000), 0), - takerFee: Web3Wrapper.toBaseUnitAmount(new BigNumber(10000), 0), - }); - const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ - makerAddress: makerAddressRight, - makerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress), - takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress), - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(89), 0), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(1), 0), - feeRecipientAddress: feeRecipientAddressRight, - }); - // Note: - // The maker/taker fee percentage paid on the left order differs because - // they received different sale prices. The left maker pays a - // fee slightly lower than the left taker. - const expectedTransferAmounts = { - // Left Maker - amountSoldByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(11), 0), - amountBoughtByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(89), 0), - feePaidByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(9166), 0), // 9166.6 rounded down to 9166 - // Right Maker - amountSoldByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(89), 0), - amountBoughtByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(1), 0), - feePaidByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% - // Taker - amountReceivedByTaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 0), - feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber(9175), 0), // 9175.2 rounded down to 9175 - feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% - }; - // Match signedOrderLeft with signedOrderRight - await matchOrderTester.matchOrdersAndAssertEffectsAsync( - signedOrderLeft, - signedOrderRight, - takerAddress, - erc20BalancesByOwner, - erc721TokenIdsByOwner, - expectedTransferAmounts, - ); - }); - - it('Should transfer correct amounts when right order fill amount deviates from amount derived by `Exchange.fillOrder`', async () => { - // Create orders to match - const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ - makerAddress: makerAddressLeft, - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(1000), 0), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(1005), 0), - feeRecipientAddress: feeRecipientAddressLeft, - }); - const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ - makerAddress: makerAddressRight, - makerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress), - takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress), - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2126), 0), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(1063), 0), - feeRecipientAddress: feeRecipientAddressRight, - }); - const expectedTransferAmounts = { - // Left Maker - amountSoldByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(1000), 0), - amountBoughtByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(1005), 0), - feePaidByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% - // Right Maker - // Notes: - // i. - // The left order is fully filled by the right order, so the right maker must sell 1005 units of their asset to the left maker. - // By selling 1005 units, the right maker should theoretically receive 502.5 units of the left maker's asset. - // Since the transfer amount must be an integer, this value must be rounded down to 502 or up to 503. - // ii. - // If the right order were filled via `Exchange.fillOrder` the respective fill amounts would be [1004, 502] or [1006, 503]. - // It follows that we cannot trigger a sale of 1005 units of the right maker's asset through `Exchange.fillOrder`. - // iii. - // For an optimal match, the algorithm must choose either [1005, 502] or [1005, 503] as fill amounts for the right order. - // The algorithm favors the right maker when the exchange rate must be rounded, so the final fill for the right order is [1005, 503]. - // iv. - // The right maker fee differs from the right taker fee because their exchange rate differs. - // The right maker always receives the better exchange and fee price. - amountSoldByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(1005), 0), - amountBoughtByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(503), 0), - feePaidByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber('47.2718720602069614'), 16), // 47.27% - // Taker - amountReceivedByTaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(497), 0), - feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% - feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber('47.3189087488240827'), 16), // 47.31% - }; - // Match signedOrderLeft with signedOrderRight - await matchOrderTester.matchOrdersAndAssertEffectsAsync( - signedOrderLeft, - signedOrderRight, - takerAddress, - erc20BalancesByOwner, - erc721TokenIdsByOwner, - expectedTransferAmounts, - ); - }); - - const reentrancyTest = (functionNames: string[]) => { - _.forEach(functionNames, async (functionName: string, functionId: number) => { - const description = `should not allow matchOrders to reenter the Exchange contract via ${functionName}`; - it(description, async () => { - const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ - makerAssetData: assetDataUtils.encodeERC20AssetData(reentrantErc20Token.address), - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - }); - const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ - makerAddress: makerAddressRight, - takerAssetData: assetDataUtils.encodeERC20AssetData(reentrantErc20Token.address), - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18), - feeRecipientAddress: feeRecipientAddressRight, - }); - await web3Wrapper.awaitTransactionSuccessAsync( - await reentrantErc20Token.setCurrentFunction.sendTransactionAsync(functionId), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - await expectTransactionFailedAsync( - exchangeWrapper.matchOrdersAsync(signedOrderLeft, signedOrderRight, takerAddress), - RevertReason.TransferFailed, - ); - }); - }); - }; - describe('matchOrders reentrancy tests', () => reentrancyTest(constants.FUNCTIONS_WITH_MUTEX)); - - it('should transfer the correct amounts when orders completely fill each other', async () => { - // Create orders to match - const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - }); - const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18), - }); - // Match signedOrderLeft with signedOrderRight - const expectedTransferAmounts = { - // Left Maker - amountSoldByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), - amountBoughtByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - feePaidByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% - // Right Maker - amountSoldByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - amountBoughtByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18), - feePaidByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% - // Taker - amountReceivedByTaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(3), 18), - feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% - feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% - }; - await matchOrderTester.matchOrdersAndAssertEffectsAsync( - signedOrderLeft, - signedOrderRight, - takerAddress, - erc20BalancesByOwner, - erc721TokenIdsByOwner, - expectedTransferAmounts, - ); - }); - - it('should transfer the correct amounts when orders completely fill each other and taker doesnt take a profit', async () => { - // Create orders to match - const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - }); - const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), - }); - // Match signedOrderLeft with signedOrderRight - const expectedTransferAmounts = { - // Left Maker - amountSoldByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), - amountBoughtByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - feePaidByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% - // Right Maker - amountSoldByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - amountBoughtByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), - feePaidByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% - // Taker - amountReceivedByTaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(0), 18), - feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% - feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% - }; - // Match signedOrderLeft with signedOrderRight - await matchOrderTester.matchOrdersAndAssertEffectsAsync( - signedOrderLeft, - signedOrderRight, - takerAddress, - erc20BalancesByOwner, - erc721TokenIdsByOwner, - expectedTransferAmounts, - ); - }); - - it('should transfer the correct amounts when left order is completely filled and right order is partially filled', async () => { - // Create orders to match - const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - }); - const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(20), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(4), 18), - }); - // Match signedOrderLeft with signedOrderRight - const expectedTransferAmounts = { - // Left Maker - amountSoldByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), - amountBoughtByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - feePaidByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% - // Right Maker - amountSoldByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - amountBoughtByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18), - feePaidByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(50), 16), // 50% - // Taker - amountReceivedByTaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(3), 18), - feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% - feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(50), 16), // 50% - }; - // Match signedOrderLeft with signedOrderRight - await matchOrderTester.matchOrdersAndAssertEffectsAsync( - signedOrderLeft, - signedOrderRight, - takerAddress, - erc20BalancesByOwner, - erc721TokenIdsByOwner, - expectedTransferAmounts, - ); - }); - - it('should transfer the correct amounts when right order is completely filled and left order is partially filled', async () => { - // Create orders to match - const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(50), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18), - }); - const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18), - }); - // Match signedOrderLeft with signedOrderRight - const expectedTransferAmounts = { - // Left Maker - amountSoldByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), - amountBoughtByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - feePaidByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 16), // 10% - // Right Maker - amountSoldByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - amountBoughtByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18), - feePaidByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% - // Taker - amountReceivedByTaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(3), 18), - feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 16), // 10% - feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% - }; - // Match signedOrderLeft with signedOrderRight - await matchOrderTester.matchOrdersAndAssertEffectsAsync( - signedOrderLeft, - signedOrderRight, - takerAddress, - erc20BalancesByOwner, - erc721TokenIdsByOwner, - expectedTransferAmounts, - ); - }); - - it('should transfer the correct amounts when consecutive calls are used to completely fill the left order', async () => { - // Create orders to match - const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(50), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18), - }); - const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18), - }); - // Match orders - let newERC20BalancesByOwner: ERC20BalancesByOwner; - let newERC721TokenIdsByOwner: ERC721TokenIdsByOwner; - const expectedTransferAmounts = { - // Left Maker - amountSoldByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), - amountBoughtByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - feePaidByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 16), // 10% - // Right Maker - amountSoldByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - amountBoughtByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18), - feePaidByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% - // Taker - amountReceivedByTaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(3), 18), - feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 16), // 10% - feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% - }; - // prettier-ignore - [ - newERC20BalancesByOwner, - // tslint:disable-next-line:trailing-comma - newERC721TokenIdsByOwner - ] = await matchOrderTester.matchOrdersAndAssertEffectsAsync( - signedOrderLeft, - signedOrderRight, - takerAddress, - erc20BalancesByOwner, - erc721TokenIdsByOwner, - expectedTransferAmounts, - ); - // Construct second right order - // Note: This order needs makerAssetAmount=90/takerAssetAmount=[anything <= 45] to fully fill the right order. - // However, we use 100/50 to ensure a partial fill as we want to go down the "left fill" - // branch in the contract twice for this test. - const signedOrderRight2 = await orderFactoryRight.newSignedOrderAsync({ - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(50), 18), - }); - // Match signedOrderLeft with signedOrderRight2 - const leftTakerAssetFilledAmount = signedOrderRight.makerAssetAmount; - const rightTakerAssetFilledAmount = new BigNumber(0); - const expectedTransferAmounts2 = { - // Left Maker - amountSoldByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(45), 18), - amountBoughtByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(90), 18), - feePaidByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(90), 16), // 90% (10% paid earlier) - // Right Maker - amountSoldByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(90), 18), - amountBoughtByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(45), 18), - feePaidByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(90), 16), // 90% - // Taker - amountReceivedByTaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(0), 18), - feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber(90), 16), // 90% (10% paid earlier) - feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(90), 16), // 90% - }; - await matchOrderTester.matchOrdersAndAssertEffectsAsync( - signedOrderLeft, - signedOrderRight2, - takerAddress, - newERC20BalancesByOwner, - newERC721TokenIdsByOwner, - expectedTransferAmounts2, - leftTakerAssetFilledAmount, - rightTakerAssetFilledAmount, - ); - }); - - it('should transfer the correct amounts when consecutive calls are used to completely fill the right order', async () => { - // Create orders to match - const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18), - }); - - const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(50), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18), - }); - // Match orders - let newERC20BalancesByOwner: ERC20BalancesByOwner; - let newERC721TokenIdsByOwner: ERC721TokenIdsByOwner; - const expectedTransferAmounts = { - // Left Maker - amountSoldByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - amountBoughtByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18), - feePaidByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% - // Right Maker - amountSoldByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18), - amountBoughtByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(4), 18), - feePaidByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(4), 16), // 4% - // Taker - amountReceivedByTaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(6), 18), - feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% - feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(4), 16), // 4% - }; - // prettier-ignore - [ - newERC20BalancesByOwner, - // tslint:disable-next-line:trailing-comma - newERC721TokenIdsByOwner - ] = await matchOrderTester.matchOrdersAndAssertEffectsAsync( - signedOrderLeft, - signedOrderRight, - takerAddress, - erc20BalancesByOwner, - erc721TokenIdsByOwner, - expectedTransferAmounts, - ); - - // Create second left order - // Note: This order needs makerAssetAmount=96/takerAssetAmount=48 to fully fill the right order. - // However, we use 100/50 to ensure a partial fill as we want to go down the "right fill" - // branch in the contract twice for this test. - const signedOrderLeft2 = await orderFactoryLeft.newSignedOrderAsync({ - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(50), 18), - }); - // Match signedOrderLeft2 with signedOrderRight - const leftTakerAssetFilledAmount = new BigNumber(0); - const takerAmountReceived = newERC20BalancesByOwner[takerAddress][defaultERC20MakerAssetAddress].minus( - erc20BalancesByOwner[takerAddress][defaultERC20MakerAssetAddress], - ); - const rightTakerAssetFilledAmount = signedOrderLeft.makerAssetAmount.minus(takerAmountReceived); - const expectedTransferAmounts2 = { - // Left Maker - amountSoldByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(96), 18), - amountBoughtByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(48), 18), - feePaidByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(96), 16), // 96% - // Right Maker - amountSoldByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(48), 18), - amountBoughtByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(96), 18), - feePaidByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(96), 16), // 96% - // Taker - amountReceivedByTaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(0), 18), - feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber(96), 16), // 96% - feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(96), 16), // 96% - }; - await matchOrderTester.matchOrdersAndAssertEffectsAsync( - signedOrderLeft2, - signedOrderRight, - takerAddress, - newERC20BalancesByOwner, - newERC721TokenIdsByOwner, - expectedTransferAmounts2, - leftTakerAssetFilledAmount, - rightTakerAssetFilledAmount, - ); - }); - - it('should transfer the correct amounts if fee recipient is the same across both matched orders', async () => { - const feeRecipientAddress = feeRecipientAddressLeft; - const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - feeRecipientAddress, - }); - const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18), - feeRecipientAddress, - }); - // Match orders - const expectedTransferAmounts = { - // Left Maker - amountSoldByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), - amountBoughtByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - feePaidByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% - // Right Maker - amountSoldByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - amountBoughtByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18), - feePaidByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% - // Taker - amountReceivedByTaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(3), 18), - feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% - feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% - }; - await matchOrderTester.matchOrdersAndAssertEffectsAsync( - signedOrderLeft, - signedOrderRight, - takerAddress, - erc20BalancesByOwner, - erc721TokenIdsByOwner, - expectedTransferAmounts, - ); - }); - - it('should transfer the correct amounts if taker is also the left order maker', async () => { - // Create orders to match - const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - }); - const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18), - }); - // Match orders - takerAddress = signedOrderLeft.makerAddress; - const expectedTransferAmounts = { - // Left Maker - amountSoldByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), - amountBoughtByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - feePaidByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% - // Right Maker - amountSoldByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - amountBoughtByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18), - feePaidByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% - // Taker - amountReceivedByTaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(3), 18), - feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% - feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% - }; - await matchOrderTester.matchOrdersAndAssertEffectsAsync( - signedOrderLeft, - signedOrderRight, - takerAddress, - erc20BalancesByOwner, - erc721TokenIdsByOwner, - expectedTransferAmounts, - ); - }); - - it('should transfer the correct amounts if taker is also the right order maker', async () => { - // Create orders to match - const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - }); - const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18), - }); - // Match orders - takerAddress = signedOrderRight.makerAddress; - const expectedTransferAmounts = { - // Left Maker - amountSoldByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), - amountBoughtByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - feePaidByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% - // Right Maker - amountSoldByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - amountBoughtByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18), - feePaidByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% - // Taker - amountReceivedByTaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(3), 18), - feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% - feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% - }; - await matchOrderTester.matchOrdersAndAssertEffectsAsync( - signedOrderLeft, - signedOrderRight, - takerAddress, - erc20BalancesByOwner, - erc721TokenIdsByOwner, - expectedTransferAmounts, - ); - }); - - it('should transfer the correct amounts if taker is also the left fee recipient', async () => { - // Create orders to match - const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - }); - const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18), - }); - // Match orders - takerAddress = feeRecipientAddressLeft; - const expectedTransferAmounts = { - // Left Maker - amountSoldByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), - amountBoughtByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - feePaidByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% - // Right Maker - amountSoldByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - amountBoughtByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18), - feePaidByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% - // Taker - amountReceivedByTaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(3), 18), - feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% - feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% - }; - await matchOrderTester.matchOrdersAndAssertEffectsAsync( - signedOrderLeft, - signedOrderRight, - takerAddress, - erc20BalancesByOwner, - erc721TokenIdsByOwner, - expectedTransferAmounts, - ); - }); - - it('should transfer the correct amounts if taker is also the right fee recipient', async () => { - // Create orders to match - const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - }); - const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18), - }); - // Match orders - takerAddress = feeRecipientAddressRight; - const expectedTransferAmounts = { - // Left Maker - amountSoldByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), - amountBoughtByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - feePaidByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% - // Right Maker - amountSoldByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - amountBoughtByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18), - feePaidByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% - // Taker - amountReceivedByTaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(3), 18), - feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% - feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% - }; - await matchOrderTester.matchOrdersAndAssertEffectsAsync( - signedOrderLeft, - signedOrderRight, - takerAddress, - erc20BalancesByOwner, - erc721TokenIdsByOwner, - expectedTransferAmounts, - ); - }); - - it('should transfer the correct amounts if left maker is the left fee recipient and right maker is the right fee recipient', async () => { - // Create orders to match - const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - }); - const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18), - }); - // Match orders - const expectedTransferAmounts = { - // Left Maker - amountSoldByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), - amountBoughtByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - feePaidByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% - // Right Maker - amountSoldByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - amountBoughtByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18), - feePaidByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% - // Taker - amountReceivedByTaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(3), 18), - feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% - feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% - }; - await matchOrderTester.matchOrdersAndAssertEffectsAsync( - signedOrderLeft, - signedOrderRight, - takerAddress, - erc20BalancesByOwner, - erc721TokenIdsByOwner, - expectedTransferAmounts, - ); - }); - - it('Should throw if left order is not fillable', async () => { - // Create orders to match - const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - }); - const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18), - }); - // Cancel left order - await exchangeWrapper.cancelOrderAsync(signedOrderLeft, signedOrderLeft.makerAddress); - // Match orders - return expectTransactionFailedAsync( - exchangeWrapper.matchOrdersAsync(signedOrderLeft, signedOrderRight, takerAddress), - RevertReason.OrderUnfillable, - ); - }); - - it('Should throw if right order is not fillable', async () => { - // Create orders to match - const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - }); - const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18), - }); - // Cancel right order - await exchangeWrapper.cancelOrderAsync(signedOrderRight, signedOrderRight.makerAddress); - // Match orders - return expectTransactionFailedAsync( - exchangeWrapper.matchOrdersAsync(signedOrderLeft, signedOrderRight, takerAddress), - RevertReason.OrderUnfillable, - ); - }); - - it('should throw if there is not a positive spread', async () => { - // Create orders to match - const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18), - }); - const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(1), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(200), 18), - }); - // Match orders - return expectTransactionFailedAsync( - exchangeWrapper.matchOrdersAsync(signedOrderLeft, signedOrderRight, takerAddress), - RevertReason.NegativeSpreadRequired, - ); - }); - - it('should throw if the left maker asset is not equal to the right taker asset ', async () => { - // Create orders to match - const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - }); - const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ - takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress), - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18), - }); - // Match orders - return expectTransactionFailedAsync( - exchangeWrapper.matchOrdersAsync(signedOrderLeft, signedOrderRight, takerAddress), - // We are assuming assetData fields of the right order are the - // reverse of the left order, rather than checking equality. This - // saves a bunch of gas, but as a result if the assetData fields are - // off then the failure ends up happening at signature validation - RevertReason.InvalidOrderSignature, - ); - }); - - it('should throw if the right maker asset is not equal to the left taker asset', async () => { - // Create orders to match - const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ - takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress), - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - }); - const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18), - }); - // Match orders - return expectTransactionFailedAsync( - exchangeWrapper.matchOrdersAsync(signedOrderLeft, signedOrderRight, takerAddress), - RevertReason.InvalidOrderSignature, - ); - }); - - it('should transfer correct amounts when left order maker asset is an ERC721 token', async () => { - // Create orders to match - const erc721TokenToTransfer = erc721LeftMakerAssetIds[0]; - const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ - makerAssetData: assetDataUtils.encodeERC721AssetData(defaultERC721AssetAddress, erc721TokenToTransfer), - makerAssetAmount: new BigNumber(1), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - }); - const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ - takerAssetData: assetDataUtils.encodeERC721AssetData(defaultERC721AssetAddress, erc721TokenToTransfer), - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - takerAssetAmount: new BigNumber(1), - }); - // Match orders - const expectedTransferAmounts = { - // Left Maker - amountSoldByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(1), 0), - amountBoughtByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - feePaidByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% - // Right Maker - amountSoldByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - amountBoughtByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(1), 0), - feePaidByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% - // Taker - amountReceivedByTaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(0), 18), - feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% - feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 50% - }; - await matchOrderTester.matchOrdersAndAssertEffectsAsync( - signedOrderLeft, - signedOrderRight, - takerAddress, - erc20BalancesByOwner, - erc721TokenIdsByOwner, - expectedTransferAmounts, - ); - }); - - it('should transfer correct amounts when right order maker asset is an ERC721 token', async () => { - // Create orders to match - const erc721TokenToTransfer = erc721RightMakerAssetIds[0]; - const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ - takerAssetData: assetDataUtils.encodeERC721AssetData(defaultERC721AssetAddress, erc721TokenToTransfer), - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - takerAssetAmount: new BigNumber(1), - }); - const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ - makerAssetData: assetDataUtils.encodeERC721AssetData(defaultERC721AssetAddress, erc721TokenToTransfer), - makerAssetAmount: new BigNumber(1), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(8), 18), - }); - // Match orders - const expectedTransferAmounts = { - // Left Maker - amountSoldByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - amountBoughtByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(1), 0), - feePaidByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% - // Right Maker - amountSoldByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(1), 0), - amountBoughtByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(8), 18), - feePaidByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% - // Taker - amountReceivedByTaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18), - feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% - feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% - }; - await matchOrderTester.matchOrdersAndAssertEffectsAsync( - signedOrderLeft, - signedOrderRight, - takerAddress, - erc20BalancesByOwner, - erc721TokenIdsByOwner, - expectedTransferAmounts, - ); - }); - }); -}); // tslint:disable-line:max-file-line-count diff --git a/contracts/core/test/exchange/signature_validator.ts b/contracts/core/test/exchange/signature_validator.ts deleted file mode 100644 index 62579cdc7..000000000 --- a/contracts/core/test/exchange/signature_validator.ts +++ /dev/null @@ -1,526 +0,0 @@ -import { artifacts as examplesArtifacts, ValidatorContract, WalletContract } from '@0x/contracts-examples'; -import { - addressUtils, - chaiSetup, - constants, - expectContractCallFailedAsync, - LogDecoder, - OrderFactory, - provider, - txDefaults, - web3Wrapper, -} from '@0x/contracts-test-utils'; -import { artifacts as tokensArtifacts } from '@0x/contracts-tokens'; -import { BlockchainLifecycle } from '@0x/dev-utils'; -import { assetDataUtils, orderHashUtils, signatureUtils } from '@0x/order-utils'; -import { RevertReason, SignatureType, SignedOrder } from '@0x/types'; -import * as chai from 'chai'; -import { LogWithDecodedArgs } from 'ethereum-types'; -import ethUtil = require('ethereumjs-util'); - -import { - artifacts, - TestSignatureValidatorContract, - TestSignatureValidatorSignatureValidatorApprovalEventArgs, - TestStaticCallReceiverContract, -} from '../../src'; - -chaiSetup.configure(); -const expect = chai.expect; - -const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); -// tslint:disable:no-unnecessary-type-assertion -describe('MixinSignatureValidator', () => { - let signedOrder: SignedOrder; - let orderFactory: OrderFactory; - let signatureValidator: TestSignatureValidatorContract; - let testWallet: WalletContract; - let testValidator: ValidatorContract; - let maliciousWallet: TestStaticCallReceiverContract; - let maliciousValidator: TestStaticCallReceiverContract; - let signerAddress: string; - let signerPrivateKey: Buffer; - let notSignerAddress: string; - let notSignerPrivateKey: Buffer; - let signatureValidatorLogDecoder: LogDecoder; - - before(async () => { - await blockchainLifecycle.startAsync(); - }); - after(async () => { - await blockchainLifecycle.revertAsync(); - }); - before(async () => { - const accounts = await web3Wrapper.getAvailableAddressesAsync(); - const makerAddress = accounts[0]; - signerAddress = makerAddress; - notSignerAddress = accounts[1]; - signatureValidator = await TestSignatureValidatorContract.deployFrom0xArtifactAsync( - artifacts.TestSignatureValidator, - provider, - txDefaults, - ); - testWallet = await WalletContract.deployFrom0xArtifactAsync( - examplesArtifacts.Wallet, - provider, - txDefaults, - signerAddress, - ); - testValidator = await ValidatorContract.deployFrom0xArtifactAsync( - examplesArtifacts.Validator, - provider, - txDefaults, - signerAddress, - ); - maliciousWallet = maliciousValidator = await TestStaticCallReceiverContract.deployFrom0xArtifactAsync( - artifacts.TestStaticCallReceiver, - provider, - txDefaults, - ); - signatureValidatorLogDecoder = new LogDecoder(web3Wrapper, { ...artifacts, ...tokensArtifacts }); - await web3Wrapper.awaitTransactionSuccessAsync( - await signatureValidator.setSignatureValidatorApproval.sendTransactionAsync(testValidator.address, true, { - from: signerAddress, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - await web3Wrapper.awaitTransactionSuccessAsync( - await signatureValidator.setSignatureValidatorApproval.sendTransactionAsync( - maliciousValidator.address, - true, - { - from: signerAddress, - }, - ), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - - const defaultOrderParams = { - ...constants.STATIC_ORDER_PARAMS, - exchangeAddress: signatureValidator.address, - makerAddress, - feeRecipientAddress: addressUtils.generatePseudoRandomAddress(), - makerAssetData: assetDataUtils.encodeERC20AssetData(addressUtils.generatePseudoRandomAddress()), - takerAssetData: assetDataUtils.encodeERC20AssetData(addressUtils.generatePseudoRandomAddress()), - }; - signerPrivateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddress)]; - notSignerPrivateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(notSignerAddress)]; - orderFactory = new OrderFactory(signerPrivateKey, defaultOrderParams); - }); - - beforeEach(async () => { - await blockchainLifecycle.startAsync(); - }); - afterEach(async () => { - await blockchainLifecycle.revertAsync(); - }); - - describe('isValidSignature', () => { - beforeEach(async () => { - signedOrder = await orderFactory.newSignedOrderAsync(); - }); - - it('should revert when signature is empty', async () => { - const emptySignature = '0x'; - const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder); - return expectContractCallFailedAsync( - signatureValidator.publicIsValidSignature.callAsync( - orderHashHex, - signedOrder.makerAddress, - emptySignature, - ), - RevertReason.LengthGreaterThan0Required, - ); - }); - - it('should revert when signature type is unsupported', async () => { - const unsupportedSignatureType = SignatureType.NSignatureTypes; - const unsupportedSignatureHex = '0x' + Buffer.from([unsupportedSignatureType]).toString('hex'); - const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder); - return expectContractCallFailedAsync( - signatureValidator.publicIsValidSignature.callAsync( - orderHashHex, - signedOrder.makerAddress, - unsupportedSignatureHex, - ), - RevertReason.SignatureUnsupported, - ); - }); - - it('should revert when SignatureType=Illegal', async () => { - const unsupportedSignatureHex = '0x' + Buffer.from([SignatureType.Illegal]).toString('hex'); - const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder); - return expectContractCallFailedAsync( - signatureValidator.publicIsValidSignature.callAsync( - orderHashHex, - signedOrder.makerAddress, - unsupportedSignatureHex, - ), - RevertReason.SignatureIllegal, - ); - }); - - it('should return false when SignatureType=Invalid and signature has a length of zero', async () => { - const signatureHex = '0x' + Buffer.from([SignatureType.Invalid]).toString('hex'); - const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder); - const isValidSignature = await signatureValidator.publicIsValidSignature.callAsync( - orderHashHex, - signedOrder.makerAddress, - signatureHex, - ); - expect(isValidSignature).to.be.false(); - }); - - it('should revert when SignatureType=Invalid and signature length is non-zero', async () => { - const fillerData = ethUtil.toBuffer('0xdeadbeef'); - const signatureType = ethUtil.toBuffer(`0x${SignatureType.Invalid}`); - const signatureBuffer = Buffer.concat([fillerData, signatureType]); - const signatureHex = ethUtil.bufferToHex(signatureBuffer); - const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder); - return expectContractCallFailedAsync( - signatureValidator.publicIsValidSignature.callAsync( - orderHashHex, - signedOrder.makerAddress, - signatureHex, - ), - RevertReason.Length0Required, - ); - }); - - it('should return true when SignatureType=EIP712 and signature is valid', async () => { - // Create EIP712 signature - const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder); - const orderHashBuffer = ethUtil.toBuffer(orderHashHex); - const ecSignature = ethUtil.ecsign(orderHashBuffer, signerPrivateKey); - // Create 0x signature from EIP712 signature - const signature = Buffer.concat([ - ethUtil.toBuffer(ecSignature.v), - ecSignature.r, - ecSignature.s, - ethUtil.toBuffer(`0x${SignatureType.EIP712}`), - ]); - const signatureHex = ethUtil.bufferToHex(signature); - // Validate signature - const isValidSignature = await signatureValidator.publicIsValidSignature.callAsync( - orderHashHex, - signerAddress, - signatureHex, - ); - expect(isValidSignature).to.be.true(); - }); - - it('should return false when SignatureType=EIP712 and signature is invalid', async () => { - // Create EIP712 signature - const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder); - const orderHashBuffer = ethUtil.toBuffer(orderHashHex); - const ecSignature = ethUtil.ecsign(orderHashBuffer, signerPrivateKey); - // Create 0x signature from EIP712 signature - const signature = Buffer.concat([ - ethUtil.toBuffer(ecSignature.v), - ecSignature.r, - ecSignature.s, - ethUtil.toBuffer(`0x${SignatureType.EIP712}`), - ]); - const signatureHex = ethUtil.bufferToHex(signature); - // Validate signature. - // This will fail because `signerAddress` signed the message, but we're passing in `notSignerAddress` - const isValidSignature = await signatureValidator.publicIsValidSignature.callAsync( - orderHashHex, - notSignerAddress, - signatureHex, - ); - expect(isValidSignature).to.be.false(); - }); - - it('should return true when SignatureType=EthSign and signature is valid', async () => { - // Create EthSign signature - const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder); - const orderHashWithEthSignPrefixHex = signatureUtils.addSignedMessagePrefix(orderHashHex); - const orderHashWithEthSignPrefixBuffer = ethUtil.toBuffer(orderHashWithEthSignPrefixHex); - const ecSignature = ethUtil.ecsign(orderHashWithEthSignPrefixBuffer, signerPrivateKey); - // Create 0x signature from EthSign signature - const signature = Buffer.concat([ - ethUtil.toBuffer(ecSignature.v), - ecSignature.r, - ecSignature.s, - ethUtil.toBuffer(`0x${SignatureType.EthSign}`), - ]); - const signatureHex = ethUtil.bufferToHex(signature); - // Validate signature - const isValidSignature = await signatureValidator.publicIsValidSignature.callAsync( - orderHashHex, - signerAddress, - signatureHex, - ); - expect(isValidSignature).to.be.true(); - }); - - it('should return false when SignatureType=EthSign and signature is invalid', async () => { - // Create EthSign signature - const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder); - const orderHashWithEthSignPrefixHex = signatureUtils.addSignedMessagePrefix(orderHashHex); - const orderHashWithEthSignPrefixBuffer = ethUtil.toBuffer(orderHashWithEthSignPrefixHex); - const ecSignature = ethUtil.ecsign(orderHashWithEthSignPrefixBuffer, signerPrivateKey); - // Create 0x signature from EthSign signature - const signature = Buffer.concat([ - ethUtil.toBuffer(ecSignature.v), - ecSignature.r, - ecSignature.s, - ethUtil.toBuffer(`0x${SignatureType.EthSign}`), - ]); - const signatureHex = ethUtil.bufferToHex(signature); - // Validate signature. - // This will fail because `signerAddress` signed the message, but we're passing in `notSignerAddress` - const isValidSignature = await signatureValidator.publicIsValidSignature.callAsync( - orderHashHex, - notSignerAddress, - signatureHex, - ); - expect(isValidSignature).to.be.false(); - }); - - it('should return true when SignatureType=Wallet and signature is valid', async () => { - // Create EIP712 signature - const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder); - const orderHashBuffer = ethUtil.toBuffer(orderHashHex); - const ecSignature = ethUtil.ecsign(orderHashBuffer, signerPrivateKey); - // Create 0x signature from EIP712 signature - const signature = Buffer.concat([ - ethUtil.toBuffer(ecSignature.v), - ecSignature.r, - ecSignature.s, - ethUtil.toBuffer(`0x${SignatureType.Wallet}`), - ]); - const signatureHex = ethUtil.bufferToHex(signature); - // Validate signature - const isValidSignature = await signatureValidator.publicIsValidSignature.callAsync( - orderHashHex, - testWallet.address, - signatureHex, - ); - expect(isValidSignature).to.be.true(); - }); - - it('should return false when SignatureType=Wallet and signature is invalid', async () => { - // Create EIP712 signature using a private key that does not belong to the wallet owner. - const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder); - const orderHashBuffer = ethUtil.toBuffer(orderHashHex); - const notWalletOwnerPrivateKey = notSignerPrivateKey; - const ecSignature = ethUtil.ecsign(orderHashBuffer, notWalletOwnerPrivateKey); - // Create 0x signature from EIP712 signature - const signature = Buffer.concat([ - ethUtil.toBuffer(ecSignature.v), - ecSignature.r, - ecSignature.s, - ethUtil.toBuffer(`0x${SignatureType.Wallet}`), - ]); - const signatureHex = ethUtil.bufferToHex(signature); - // Validate signature - const isValidSignature = await signatureValidator.publicIsValidSignature.callAsync( - orderHashHex, - testWallet.address, - signatureHex, - ); - expect(isValidSignature).to.be.false(); - }); - - it('should revert when `isValidSignature` attempts to update state and SignatureType=Wallet', async () => { - // Create EIP712 signature - const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder); - const orderHashBuffer = ethUtil.toBuffer(orderHashHex); - const ecSignature = ethUtil.ecsign(orderHashBuffer, signerPrivateKey); - // Create 0x signature from EIP712 signature - const signature = Buffer.concat([ - ethUtil.toBuffer(ecSignature.v), - ecSignature.r, - ecSignature.s, - ethUtil.toBuffer(`0x${SignatureType.Wallet}`), - ]); - const signatureHex = ethUtil.bufferToHex(signature); - await expectContractCallFailedAsync( - signatureValidator.publicIsValidSignature.callAsync( - orderHashHex, - maliciousWallet.address, - signatureHex, - ), - RevertReason.WalletError, - ); - }); - - it('should return true when SignatureType=Validator, signature is valid and validator is approved', async () => { - const validatorAddress = ethUtil.toBuffer(`${testValidator.address}`); - const signatureType = ethUtil.toBuffer(`0x${SignatureType.Validator}`); - const signature = Buffer.concat([validatorAddress, signatureType]); - const signatureHex = ethUtil.bufferToHex(signature); - const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder); - const isValidSignature = await signatureValidator.publicIsValidSignature.callAsync( - orderHashHex, - signerAddress, - signatureHex, - ); - expect(isValidSignature).to.be.true(); - }); - - it('should return false when SignatureType=Validator, signature is invalid and validator is approved', async () => { - const validatorAddress = ethUtil.toBuffer(`${testValidator.address}`); - const signatureType = ethUtil.toBuffer(`0x${SignatureType.Validator}`); - const signature = Buffer.concat([validatorAddress, signatureType]); - const signatureHex = ethUtil.bufferToHex(signature); - const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder); - // This will return false because we signed the message with `signerAddress`, but - // are validating against `notSignerAddress` - const isValidSignature = await signatureValidator.publicIsValidSignature.callAsync( - orderHashHex, - notSignerAddress, - signatureHex, - ); - expect(isValidSignature).to.be.false(); - }); - - it('should revert when `isValidSignature` attempts to update state and SignatureType=Validator', async () => { - const validatorAddress = ethUtil.toBuffer(`${maliciousValidator.address}`); - const signatureType = ethUtil.toBuffer(`0x${SignatureType.Validator}`); - const signature = Buffer.concat([validatorAddress, signatureType]); - const signatureHex = ethUtil.bufferToHex(signature); - const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder); - await expectContractCallFailedAsync( - signatureValidator.publicIsValidSignature.callAsync(orderHashHex, signerAddress, signatureHex), - RevertReason.ValidatorError, - ); - }); - it('should return false when SignatureType=Validator, signature is valid and validator is not approved', async () => { - // Set approval of signature validator to false - await web3Wrapper.awaitTransactionSuccessAsync( - await signatureValidator.setSignatureValidatorApproval.sendTransactionAsync( - testValidator.address, - false, - { from: signerAddress }, - ), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - // Validate signature - const validatorAddress = ethUtil.toBuffer(`${testValidator.address}`); - const signatureType = ethUtil.toBuffer(`0x${SignatureType.Validator}`); - const signature = Buffer.concat([validatorAddress, signatureType]); - const signatureHex = ethUtil.bufferToHex(signature); - const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder); - const isValidSignature = await signatureValidator.publicIsValidSignature.callAsync( - orderHashHex, - signerAddress, - signatureHex, - ); - expect(isValidSignature).to.be.false(); - }); - - it('should return true when SignatureType=Presigned and signer has presigned hash', async () => { - // Presign hash - const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder); - await web3Wrapper.awaitTransactionSuccessAsync( - await signatureValidator.preSign.sendTransactionAsync( - orderHashHex, - signedOrder.makerAddress, - signedOrder.signature, - ), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - // Validate presigned signature - const signature = ethUtil.toBuffer(`0x${SignatureType.PreSigned}`); - const signatureHex = ethUtil.bufferToHex(signature); - const isValidSignature = await signatureValidator.publicIsValidSignature.callAsync( - orderHashHex, - signedOrder.makerAddress, - signatureHex, - ); - expect(isValidSignature).to.be.true(); - }); - - it('should return false when SignatureType=Presigned and signer has not presigned hash', async () => { - const signature = ethUtil.toBuffer(`0x${SignatureType.PreSigned}`); - const signatureHex = ethUtil.bufferToHex(signature); - const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder); - const isValidSignature = await signatureValidator.publicIsValidSignature.callAsync( - orderHashHex, - signedOrder.makerAddress, - signatureHex, - ); - expect(isValidSignature).to.be.false(); - }); - - it('should return true when message was signed by a Trezor One (firmware version 1.6.2)', async () => { - // messageHash translates to 0x2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b - const messageHash = ethUtil.bufferToHex(ethUtil.toBuffer('++++++++++++++++++++++++++++++++')); - const signer = '0xc28b145f10f0bcf0fc000e778615f8fd73490bad'; - const v = ethUtil.toBuffer('0x1c'); - const r = ethUtil.toBuffer('0x7b888b596ccf87f0bacab0dcb483124973f7420f169b4824d7a12534ac1e9832'); - const s = ethUtil.toBuffer('0x0c8e14f7edc01459e13965f1da56e0c23ed11e2cca932571eee1292178f90424'); - const trezorSignatureType = ethUtil.toBuffer(`0x${SignatureType.EthSign}`); - const signature = Buffer.concat([v, r, s, trezorSignatureType]); - const signatureHex = ethUtil.bufferToHex(signature); - const isValidSignature = await signatureValidator.publicIsValidSignature.callAsync( - messageHash, - signer, - signatureHex, - ); - expect(isValidSignature).to.be.true(); - }); - - it('should return true when message was signed by a Trezor Model T (firmware version 2.0.7)', async () => { - // messageHash translates to 0x2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b - const messageHash = ethUtil.bufferToHex(ethUtil.toBuffer('++++++++++++++++++++++++++++++++')); - const signer = '0x98ce6d9345e8ffa7d99ee0822272fae9d2c0e895'; - const v = ethUtil.toBuffer('0x1c'); - const r = ethUtil.toBuffer('0x423b71062c327f0ec4fe199b8da0f34185e59b4c1cb4cc23df86cac4a601fb3f'); - const s = ethUtil.toBuffer('0x53810d6591b5348b7ee08ee812c874b0fdfb942c9849d59512c90e295221091f'); - const trezorSignatureType = ethUtil.toBuffer(`0x${SignatureType.EthSign}`); - const signature = Buffer.concat([v, r, s, trezorSignatureType]); - const signatureHex = ethUtil.bufferToHex(signature); - const isValidSignature = await signatureValidator.publicIsValidSignature.callAsync( - messageHash, - signer, - signatureHex, - ); - expect(isValidSignature).to.be.true(); - }); - }); - - describe('setSignatureValidatorApproval', () => { - it('should emit a SignatureValidatorApprovalSet with correct args when a validator is approved', async () => { - const approval = true; - const res = await signatureValidatorLogDecoder.getTxWithDecodedLogsAsync( - await signatureValidator.setSignatureValidatorApproval.sendTransactionAsync( - testValidator.address, - approval, - { - from: signerAddress, - }, - ), - ); - expect(res.logs.length).to.equal(1); - const log = res.logs[0] as LogWithDecodedArgs<TestSignatureValidatorSignatureValidatorApprovalEventArgs>; - const logArgs = log.args; - expect(logArgs.signerAddress).to.equal(signerAddress); - expect(logArgs.validatorAddress).to.equal(testValidator.address); - expect(logArgs.approved).to.equal(approval); - }); - it('should emit a SignatureValidatorApprovalSet with correct args when a validator is disapproved', async () => { - const approval = false; - const res = await signatureValidatorLogDecoder.getTxWithDecodedLogsAsync( - await signatureValidator.setSignatureValidatorApproval.sendTransactionAsync( - testValidator.address, - approval, - { - from: signerAddress, - }, - ), - ); - expect(res.logs.length).to.equal(1); - const log = res.logs[0] as LogWithDecodedArgs<TestSignatureValidatorSignatureValidatorApprovalEventArgs>; - const logArgs = log.args; - expect(logArgs.signerAddress).to.equal(signerAddress); - expect(logArgs.validatorAddress).to.equal(testValidator.address); - expect(logArgs.approved).to.equal(approval); - }); - }); -}); -// tslint:disable:max-file-line-count -// tslint:enable:no-unnecessary-type-assertion diff --git a/contracts/core/test/exchange/transactions.ts b/contracts/core/test/exchange/transactions.ts deleted file mode 100644 index 746f3cb04..000000000 --- a/contracts/core/test/exchange/transactions.ts +++ /dev/null @@ -1,466 +0,0 @@ -import { artifacts as examplesArtifacts, ExchangeWrapperContract, WhitelistContract } from '@0x/contracts-examples'; -import { - chaiSetup, - constants, - ERC20BalancesByOwner, - expectTransactionFailedAsync, - OrderFactory, - orderUtils, - provider, - SignedTransaction, - TransactionFactory, - txDefaults, - web3Wrapper, -} from '@0x/contracts-test-utils'; -import { DummyERC20TokenContract } from '@0x/contracts-tokens'; -import { BlockchainLifecycle } from '@0x/dev-utils'; -import { assetDataUtils, generatePseudoRandomSalt } from '@0x/order-utils'; -import { OrderWithoutExchangeAddress, RevertReason, SignedOrder } from '@0x/types'; -import { BigNumber } from '@0x/utils'; -import * as chai from 'chai'; -import * as _ from 'lodash'; - -import { ERC20ProxyContract } from '../../generated-wrappers/erc20_proxy'; -import { ExchangeContract } from '../../generated-wrappers/exchange'; -import { artifacts } from '../../src/artifacts'; -import { ERC20Wrapper } from '../utils/erc20_wrapper'; -import { ExchangeWrapper } from '../utils/exchange_wrapper'; - -chaiSetup.configure(); -const expect = chai.expect; -const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); - -describe('Exchange transactions', () => { - let senderAddress: string; - let owner: string; - let makerAddress: string; - let takerAddress: string; - let feeRecipientAddress: string; - - let erc20TokenA: DummyERC20TokenContract; - let erc20TokenB: DummyERC20TokenContract; - let zrxToken: DummyERC20TokenContract; - let exchange: ExchangeContract; - let erc20Proxy: ERC20ProxyContract; - - let erc20Balances: ERC20BalancesByOwner; - let signedOrder: SignedOrder; - let signedTx: SignedTransaction; - let orderWithoutExchangeAddress: OrderWithoutExchangeAddress; - let orderFactory: OrderFactory; - let makerTransactionFactory: TransactionFactory; - let takerTransactionFactory: TransactionFactory; - let exchangeWrapper: ExchangeWrapper; - let erc20Wrapper: ERC20Wrapper; - - let defaultMakerTokenAddress: string; - let defaultTakerTokenAddress: string; - let makerPrivateKey: Buffer; - let takerPrivateKey: Buffer; - - before(async () => { - await blockchainLifecycle.startAsync(); - }); - after(async () => { - await blockchainLifecycle.revertAsync(); - }); - beforeEach(async () => { - await blockchainLifecycle.startAsync(); - }); - afterEach(async () => { - await blockchainLifecycle.revertAsync(); - }); - before(async () => { - const accounts = await web3Wrapper.getAvailableAddressesAsync(); - const usedAddresses = ([owner, senderAddress, makerAddress, takerAddress, feeRecipientAddress] = _.slice( - accounts, - 0, - 5, - )); - - erc20Wrapper = new ERC20Wrapper(provider, usedAddresses, owner); - - const numDummyErc20ToDeploy = 3; - [erc20TokenA, erc20TokenB, zrxToken] = await erc20Wrapper.deployDummyTokensAsync( - numDummyErc20ToDeploy, - constants.DUMMY_TOKEN_DECIMALS, - ); - erc20Proxy = await erc20Wrapper.deployProxyAsync(); - await erc20Wrapper.setBalancesAndAllowancesAsync(); - - exchange = await ExchangeContract.deployFrom0xArtifactAsync( - artifacts.Exchange, - provider, - txDefaults, - assetDataUtils.encodeERC20AssetData(zrxToken.address), - ); - exchangeWrapper = new ExchangeWrapper(exchange, provider); - await exchangeWrapper.registerAssetProxyAsync(erc20Proxy.address, owner); - - await web3Wrapper.awaitTransactionSuccessAsync( - await erc20Proxy.addAuthorizedAddress.sendTransactionAsync(exchange.address, { from: owner }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - - defaultMakerTokenAddress = erc20TokenA.address; - defaultTakerTokenAddress = erc20TokenB.address; - - const defaultOrderParams = { - ...constants.STATIC_ORDER_PARAMS, - senderAddress, - exchangeAddress: exchange.address, - makerAddress, - feeRecipientAddress, - makerAssetData: assetDataUtils.encodeERC20AssetData(defaultMakerTokenAddress), - takerAssetData: assetDataUtils.encodeERC20AssetData(defaultTakerTokenAddress), - }; - makerPrivateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddress)]; - takerPrivateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(takerAddress)]; - orderFactory = new OrderFactory(makerPrivateKey, defaultOrderParams); - makerTransactionFactory = new TransactionFactory(makerPrivateKey, exchange.address); - takerTransactionFactory = new TransactionFactory(takerPrivateKey, exchange.address); - }); - describe('executeTransaction', () => { - describe('fillOrder', () => { - let takerAssetFillAmount: BigNumber; - beforeEach(async () => { - erc20Balances = await erc20Wrapper.getBalancesAsync(); - signedOrder = await orderFactory.newSignedOrderAsync(); - orderWithoutExchangeAddress = orderUtils.getOrderWithoutExchangeAddress(signedOrder); - - takerAssetFillAmount = signedOrder.takerAssetAmount.div(2); - const data = exchange.fillOrder.getABIEncodedTransactionData( - orderWithoutExchangeAddress, - takerAssetFillAmount, - signedOrder.signature, - ); - signedTx = takerTransactionFactory.newSignedTransaction(data); - }); - - it('should throw if not called by specified sender', async () => { - return expectTransactionFailedAsync( - exchangeWrapper.executeTransactionAsync(signedTx, takerAddress), - RevertReason.FailedExecution, - ); - }); - - it('should transfer the correct amounts when signed by taker and called by sender', async () => { - await exchangeWrapper.executeTransactionAsync(signedTx, senderAddress); - const newBalances = await erc20Wrapper.getBalancesAsync(); - const makerAssetFillAmount = takerAssetFillAmount - .times(signedOrder.makerAssetAmount) - .dividedToIntegerBy(signedOrder.takerAssetAmount); - const makerFeePaid = signedOrder.makerFee - .times(makerAssetFillAmount) - .dividedToIntegerBy(signedOrder.makerAssetAmount); - const takerFeePaid = signedOrder.takerFee - .times(makerAssetFillAmount) - .dividedToIntegerBy(signedOrder.makerAssetAmount); - expect(newBalances[makerAddress][defaultMakerTokenAddress]).to.be.bignumber.equal( - erc20Balances[makerAddress][defaultMakerTokenAddress].minus(makerAssetFillAmount), - ); - expect(newBalances[makerAddress][defaultTakerTokenAddress]).to.be.bignumber.equal( - erc20Balances[makerAddress][defaultTakerTokenAddress].add(takerAssetFillAmount), - ); - expect(newBalances[makerAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[makerAddress][zrxToken.address].minus(makerFeePaid), - ); - expect(newBalances[takerAddress][defaultTakerTokenAddress]).to.be.bignumber.equal( - erc20Balances[takerAddress][defaultTakerTokenAddress].minus(takerAssetFillAmount), - ); - expect(newBalances[takerAddress][defaultMakerTokenAddress]).to.be.bignumber.equal( - erc20Balances[takerAddress][defaultMakerTokenAddress].add(makerAssetFillAmount), - ); - expect(newBalances[takerAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[takerAddress][zrxToken.address].minus(takerFeePaid), - ); - expect(newBalances[feeRecipientAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[feeRecipientAddress][zrxToken.address].add(makerFeePaid.add(takerFeePaid)), - ); - }); - - it('should throw if the a 0x transaction with the same transactionHash has already been executed', async () => { - await exchangeWrapper.executeTransactionAsync(signedTx, senderAddress); - return expectTransactionFailedAsync( - exchangeWrapper.executeTransactionAsync(signedTx, senderAddress), - RevertReason.InvalidTxHash, - ); - }); - - it('should reset the currentContextAddress', async () => { - await exchangeWrapper.executeTransactionAsync(signedTx, senderAddress); - const currentContextAddress = await exchange.currentContextAddress.callAsync(); - expect(currentContextAddress).to.equal(constants.NULL_ADDRESS); - }); - }); - - describe('cancelOrder', () => { - beforeEach(async () => { - const data = exchange.cancelOrder.getABIEncodedTransactionData(orderWithoutExchangeAddress); - signedTx = makerTransactionFactory.newSignedTransaction(data); - }); - - it('should throw if not called by specified sender', async () => { - return expectTransactionFailedAsync( - exchangeWrapper.executeTransactionAsync(signedTx, makerAddress), - RevertReason.FailedExecution, - ); - }); - - it('should cancel the order when signed by maker and called by sender', async () => { - await exchangeWrapper.executeTransactionAsync(signedTx, senderAddress); - return expectTransactionFailedAsync( - exchangeWrapper.fillOrderAsync(signedOrder, senderAddress), - RevertReason.OrderUnfillable, - ); - }); - }); - - describe('cancelOrdersUpTo', () => { - let exchangeWrapperContract: ExchangeWrapperContract; - - before(async () => { - exchangeWrapperContract = await ExchangeWrapperContract.deployFrom0xArtifactAsync( - examplesArtifacts.ExchangeWrapper, - provider, - txDefaults, - exchange.address, - ); - }); - - it("should cancel an order if called from the order's sender", async () => { - const orderSalt = new BigNumber(0); - signedOrder = await orderFactory.newSignedOrderAsync({ - senderAddress: exchangeWrapperContract.address, - salt: orderSalt, - }); - const targetOrderEpoch = orderSalt.add(1); - const cancelData = exchange.cancelOrdersUpTo.getABIEncodedTransactionData(targetOrderEpoch); - const signedCancelTx = makerTransactionFactory.newSignedTransaction(cancelData); - await exchangeWrapperContract.cancelOrdersUpTo.sendTransactionAsync( - targetOrderEpoch, - signedCancelTx.salt, - signedCancelTx.signature, - { - from: makerAddress, - }, - ); - - const takerAssetFillAmount = signedOrder.takerAssetAmount; - orderWithoutExchangeAddress = orderUtils.getOrderWithoutExchangeAddress(signedOrder); - const fillData = exchange.fillOrder.getABIEncodedTransactionData( - orderWithoutExchangeAddress, - takerAssetFillAmount, - signedOrder.signature, - ); - const signedFillTx = takerTransactionFactory.newSignedTransaction(fillData); - return expectTransactionFailedAsync( - exchangeWrapperContract.fillOrder.sendTransactionAsync( - orderWithoutExchangeAddress, - takerAssetFillAmount, - signedFillTx.salt, - signedOrder.signature, - signedFillTx.signature, - { from: takerAddress }, - ), - RevertReason.FailedExecution, - ); - }); - - it("should not cancel an order if not called from the order's sender", async () => { - const orderSalt = new BigNumber(0); - signedOrder = await orderFactory.newSignedOrderAsync({ - senderAddress: exchangeWrapperContract.address, - salt: orderSalt, - }); - const targetOrderEpoch = orderSalt.add(1); - await exchangeWrapper.cancelOrdersUpToAsync(targetOrderEpoch, makerAddress); - - erc20Balances = await erc20Wrapper.getBalancesAsync(); - const takerAssetFillAmount = signedOrder.takerAssetAmount; - orderWithoutExchangeAddress = orderUtils.getOrderWithoutExchangeAddress(signedOrder); - const data = exchange.fillOrder.getABIEncodedTransactionData( - orderWithoutExchangeAddress, - takerAssetFillAmount, - signedOrder.signature, - ); - signedTx = takerTransactionFactory.newSignedTransaction(data); - await exchangeWrapperContract.fillOrder.sendTransactionAsync( - orderWithoutExchangeAddress, - takerAssetFillAmount, - signedTx.salt, - signedOrder.signature, - signedTx.signature, - { from: takerAddress }, - ); - - const newBalances = await erc20Wrapper.getBalancesAsync(); - const makerAssetFillAmount = takerAssetFillAmount - .times(signedOrder.makerAssetAmount) - .dividedToIntegerBy(signedOrder.takerAssetAmount); - const makerFeePaid = signedOrder.makerFee - .times(makerAssetFillAmount) - .dividedToIntegerBy(signedOrder.makerAssetAmount); - const takerFeePaid = signedOrder.takerFee - .times(makerAssetFillAmount) - .dividedToIntegerBy(signedOrder.makerAssetAmount); - expect(newBalances[makerAddress][defaultMakerTokenAddress]).to.be.bignumber.equal( - erc20Balances[makerAddress][defaultMakerTokenAddress].minus(makerAssetFillAmount), - ); - expect(newBalances[makerAddress][defaultTakerTokenAddress]).to.be.bignumber.equal( - erc20Balances[makerAddress][defaultTakerTokenAddress].add(takerAssetFillAmount), - ); - expect(newBalances[makerAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[makerAddress][zrxToken.address].minus(makerFeePaid), - ); - expect(newBalances[takerAddress][defaultTakerTokenAddress]).to.be.bignumber.equal( - erc20Balances[takerAddress][defaultTakerTokenAddress].minus(takerAssetFillAmount), - ); - expect(newBalances[takerAddress][defaultMakerTokenAddress]).to.be.bignumber.equal( - erc20Balances[takerAddress][defaultMakerTokenAddress].add(makerAssetFillAmount), - ); - expect(newBalances[takerAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[takerAddress][zrxToken.address].minus(takerFeePaid), - ); - expect(newBalances[feeRecipientAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[feeRecipientAddress][zrxToken.address].add(makerFeePaid.add(takerFeePaid)), - ); - }); - }); - }); - - describe('Whitelist', () => { - let whitelist: WhitelistContract; - let whitelistOrderFactory: OrderFactory; - - before(async () => { - whitelist = await WhitelistContract.deployFrom0xArtifactAsync( - examplesArtifacts.Whitelist, - provider, - txDefaults, - exchange.address, - ); - const isApproved = true; - await web3Wrapper.awaitTransactionSuccessAsync( - await exchange.setSignatureValidatorApproval.sendTransactionAsync(whitelist.address, isApproved, { - from: takerAddress, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - const defaultOrderParams = { - ...constants.STATIC_ORDER_PARAMS, - senderAddress: whitelist.address, - exchangeAddress: exchange.address, - makerAddress, - feeRecipientAddress, - makerAssetData: assetDataUtils.encodeERC20AssetData(defaultMakerTokenAddress), - takerAssetData: assetDataUtils.encodeERC20AssetData(defaultTakerTokenAddress), - }; - whitelistOrderFactory = new OrderFactory(makerPrivateKey, defaultOrderParams); - }); - - beforeEach(async () => { - signedOrder = await whitelistOrderFactory.newSignedOrderAsync(); - erc20Balances = await erc20Wrapper.getBalancesAsync(); - }); - - it('should revert if maker has not been whitelisted', async () => { - const isApproved = true; - await web3Wrapper.awaitTransactionSuccessAsync( - await whitelist.updateWhitelistStatus.sendTransactionAsync(takerAddress, isApproved, { from: owner }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - - orderWithoutExchangeAddress = orderUtils.getOrderWithoutExchangeAddress(signedOrder); - const takerAssetFillAmount = signedOrder.takerAssetAmount; - const salt = generatePseudoRandomSalt(); - return expectTransactionFailedAsync( - whitelist.fillOrderIfWhitelisted.sendTransactionAsync( - orderWithoutExchangeAddress, - takerAssetFillAmount, - salt, - signedOrder.signature, - { from: takerAddress }, - ), - RevertReason.MakerNotWhitelisted, - ); - }); - - it('should revert if taker has not been whitelisted', async () => { - const isApproved = true; - await web3Wrapper.awaitTransactionSuccessAsync( - await whitelist.updateWhitelistStatus.sendTransactionAsync(makerAddress, isApproved, { from: owner }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - - orderWithoutExchangeAddress = orderUtils.getOrderWithoutExchangeAddress(signedOrder); - const takerAssetFillAmount = signedOrder.takerAssetAmount; - const salt = generatePseudoRandomSalt(); - return expectTransactionFailedAsync( - whitelist.fillOrderIfWhitelisted.sendTransactionAsync( - orderWithoutExchangeAddress, - takerAssetFillAmount, - salt, - signedOrder.signature, - { from: takerAddress }, - ), - RevertReason.TakerNotWhitelisted, - ); - }); - - it('should fill the order if maker and taker have been whitelisted', async () => { - const isApproved = true; - await web3Wrapper.awaitTransactionSuccessAsync( - await whitelist.updateWhitelistStatus.sendTransactionAsync(makerAddress, isApproved, { from: owner }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - - await web3Wrapper.awaitTransactionSuccessAsync( - await whitelist.updateWhitelistStatus.sendTransactionAsync(takerAddress, isApproved, { from: owner }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - - orderWithoutExchangeAddress = orderUtils.getOrderWithoutExchangeAddress(signedOrder); - const takerAssetFillAmount = signedOrder.takerAssetAmount; - const salt = generatePseudoRandomSalt(); - await web3Wrapper.awaitTransactionSuccessAsync( - await whitelist.fillOrderIfWhitelisted.sendTransactionAsync( - orderWithoutExchangeAddress, - takerAssetFillAmount, - salt, - signedOrder.signature, - { from: takerAddress }, - ), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - - const newBalances = await erc20Wrapper.getBalancesAsync(); - - const makerAssetFillAmount = signedOrder.makerAssetAmount; - const makerFeePaid = signedOrder.makerFee; - const takerFeePaid = signedOrder.takerFee; - - expect(newBalances[makerAddress][defaultMakerTokenAddress]).to.be.bignumber.equal( - erc20Balances[makerAddress][defaultMakerTokenAddress].minus(makerAssetFillAmount), - ); - expect(newBalances[makerAddress][defaultTakerTokenAddress]).to.be.bignumber.equal( - erc20Balances[makerAddress][defaultTakerTokenAddress].add(takerAssetFillAmount), - ); - expect(newBalances[makerAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[makerAddress][zrxToken.address].minus(makerFeePaid), - ); - expect(newBalances[takerAddress][defaultTakerTokenAddress]).to.be.bignumber.equal( - erc20Balances[takerAddress][defaultTakerTokenAddress].minus(takerAssetFillAmount), - ); - expect(newBalances[takerAddress][defaultMakerTokenAddress]).to.be.bignumber.equal( - erc20Balances[takerAddress][defaultMakerTokenAddress].add(makerAssetFillAmount), - ); - expect(newBalances[takerAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[takerAddress][zrxToken.address].minus(takerFeePaid), - ); - expect(newBalances[feeRecipientAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[feeRecipientAddress][zrxToken.address].add(makerFeePaid.add(takerFeePaid)), - ); - }); - }); -}); diff --git a/contracts/core/test/exchange/wrapper.ts b/contracts/core/test/exchange/wrapper.ts deleted file mode 100644 index c9efb0c9d..000000000 --- a/contracts/core/test/exchange/wrapper.ts +++ /dev/null @@ -1,1461 +0,0 @@ -import { - chaiSetup, - constants, - ERC20BalancesByOwner, - expectTransactionFailedAsync, - getLatestBlockTimestampAsync, - increaseTimeAndMineBlockAsync, - OrderFactory, - OrderStatus, - provider, - txDefaults, - web3Wrapper, -} from '@0x/contracts-test-utils'; -import { - artifacts as tokensArtifacts, - DummyERC20TokenContract, - DummyERC721TokenContract, - ReentrantERC20TokenContract, -} from '@0x/contracts-tokens'; -import { BlockchainLifecycle } from '@0x/dev-utils'; -import { assetDataUtils, orderHashUtils } from '@0x/order-utils'; -import { RevertReason, SignedOrder } from '@0x/types'; -import { BigNumber } from '@0x/utils'; -import { Web3Wrapper } from '@0x/web3-wrapper'; -import * as chai from 'chai'; -import * as _ from 'lodash'; - -import { ERC20ProxyContract } from '../../generated-wrappers/erc20_proxy'; -import { ERC721ProxyContract } from '../../generated-wrappers/erc721_proxy'; -import { ExchangeContract } from '../../generated-wrappers/exchange'; -import { artifacts } from '../../src/artifacts'; -import { ERC20Wrapper } from '../utils/erc20_wrapper'; -import { ERC721Wrapper } from '../utils/erc721_wrapper'; -import { ExchangeWrapper } from '../utils/exchange_wrapper'; - -chaiSetup.configure(); -const expect = chai.expect; -const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); - -describe('Exchange wrappers', () => { - let makerAddress: string; - let owner: string; - let takerAddress: string; - let feeRecipientAddress: string; - - let erc20TokenA: DummyERC20TokenContract; - let erc20TokenB: DummyERC20TokenContract; - let zrxToken: DummyERC20TokenContract; - let erc721Token: DummyERC721TokenContract; - let exchange: ExchangeContract; - let erc20Proxy: ERC20ProxyContract; - let erc721Proxy: ERC721ProxyContract; - let reentrantErc20Token: ReentrantERC20TokenContract; - - let exchangeWrapper: ExchangeWrapper; - let erc20Wrapper: ERC20Wrapper; - let erc721Wrapper: ERC721Wrapper; - let erc20Balances: ERC20BalancesByOwner; - let orderFactory: OrderFactory; - - let erc721MakerAssetId: BigNumber; - let erc721TakerAssetId: BigNumber; - - let defaultMakerAssetAddress: string; - let defaultTakerAssetAddress: string; - - before(async () => { - await blockchainLifecycle.startAsync(); - }); - after(async () => { - await blockchainLifecycle.revertAsync(); - }); - before(async () => { - const accounts = await web3Wrapper.getAvailableAddressesAsync(); - const usedAddresses = ([owner, makerAddress, takerAddress, feeRecipientAddress] = _.slice(accounts, 0, 4)); - - erc20Wrapper = new ERC20Wrapper(provider, usedAddresses, owner); - erc721Wrapper = new ERC721Wrapper(provider, usedAddresses, owner); - - const numDummyErc20ToDeploy = 3; - [erc20TokenA, erc20TokenB, zrxToken] = await erc20Wrapper.deployDummyTokensAsync( - numDummyErc20ToDeploy, - constants.DUMMY_TOKEN_DECIMALS, - ); - erc20Proxy = await erc20Wrapper.deployProxyAsync(); - await erc20Wrapper.setBalancesAndAllowancesAsync(); - - [erc721Token] = await erc721Wrapper.deployDummyTokensAsync(); - erc721Proxy = await erc721Wrapper.deployProxyAsync(); - await erc721Wrapper.setBalancesAndAllowancesAsync(); - const erc721Balances = await erc721Wrapper.getBalancesAsync(); - erc721MakerAssetId = erc721Balances[makerAddress][erc721Token.address][0]; - erc721TakerAssetId = erc721Balances[takerAddress][erc721Token.address][0]; - - exchange = await ExchangeContract.deployFrom0xArtifactAsync( - artifacts.Exchange, - provider, - txDefaults, - assetDataUtils.encodeERC20AssetData(zrxToken.address), - ); - exchangeWrapper = new ExchangeWrapper(exchange, provider); - await exchangeWrapper.registerAssetProxyAsync(erc20Proxy.address, owner); - await exchangeWrapper.registerAssetProxyAsync(erc721Proxy.address, owner); - - await web3Wrapper.awaitTransactionSuccessAsync( - await erc20Proxy.addAuthorizedAddress.sendTransactionAsync(exchange.address, { - from: owner, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - await web3Wrapper.awaitTransactionSuccessAsync( - await erc721Proxy.addAuthorizedAddress.sendTransactionAsync(exchange.address, { - from: owner, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - - reentrantErc20Token = await ReentrantERC20TokenContract.deployFrom0xArtifactAsync( - tokensArtifacts.ReentrantERC20Token, - provider, - txDefaults, - exchange.address, - ); - - defaultMakerAssetAddress = erc20TokenA.address; - defaultTakerAssetAddress = erc20TokenB.address; - - const defaultOrderParams = { - ...constants.STATIC_ORDER_PARAMS, - exchangeAddress: exchange.address, - makerAddress, - feeRecipientAddress, - makerAssetData: assetDataUtils.encodeERC20AssetData(defaultMakerAssetAddress), - takerAssetData: assetDataUtils.encodeERC20AssetData(defaultTakerAssetAddress), - }; - const privateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddress)]; - orderFactory = new OrderFactory(privateKey, defaultOrderParams); - }); - beforeEach(async () => { - await blockchainLifecycle.startAsync(); - erc20Balances = await erc20Wrapper.getBalancesAsync(); - }); - afterEach(async () => { - await blockchainLifecycle.revertAsync(); - }); - describe('fillOrKillOrder', () => { - const reentrancyTest = (functionNames: string[]) => { - _.forEach(functionNames, async (functionName: string, functionId: number) => { - const description = `should not allow fillOrKillOrder to reenter the Exchange contract via ${functionName}`; - it(description, async () => { - const signedOrder = await orderFactory.newSignedOrderAsync({ - makerAssetData: assetDataUtils.encodeERC20AssetData(reentrantErc20Token.address), - }); - await web3Wrapper.awaitTransactionSuccessAsync( - await reentrantErc20Token.setCurrentFunction.sendTransactionAsync(functionId), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - await expectTransactionFailedAsync( - exchangeWrapper.fillOrKillOrderAsync(signedOrder, takerAddress), - RevertReason.TransferFailed, - ); - }); - }); - }; - describe('fillOrKillOrder reentrancy tests', () => reentrancyTest(constants.FUNCTIONS_WITH_MUTEX)); - - it('should transfer the correct amounts', async () => { - const signedOrder = await orderFactory.newSignedOrderAsync({ - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(200), 18), - }); - const takerAssetFillAmount = signedOrder.takerAssetAmount.div(2); - await exchangeWrapper.fillOrKillOrderAsync(signedOrder, takerAddress, { - takerAssetFillAmount, - }); - - const newBalances = await erc20Wrapper.getBalancesAsync(); - - const makerAssetFilledAmount = takerAssetFillAmount - .times(signedOrder.makerAssetAmount) - .dividedToIntegerBy(signedOrder.takerAssetAmount); - const makerFee = signedOrder.makerFee - .times(makerAssetFilledAmount) - .dividedToIntegerBy(signedOrder.makerAssetAmount); - const takerFee = signedOrder.takerFee - .times(makerAssetFilledAmount) - .dividedToIntegerBy(signedOrder.makerAssetAmount); - expect(newBalances[makerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[makerAddress][defaultMakerAssetAddress].minus(makerAssetFilledAmount), - ); - expect(newBalances[makerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[makerAddress][defaultTakerAssetAddress].add(takerAssetFillAmount), - ); - expect(newBalances[makerAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[makerAddress][zrxToken.address].minus(makerFee), - ); - expect(newBalances[takerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[takerAddress][defaultTakerAssetAddress].minus(takerAssetFillAmount), - ); - expect(newBalances[takerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[takerAddress][defaultMakerAssetAddress].add(makerAssetFilledAmount), - ); - expect(newBalances[takerAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[takerAddress][zrxToken.address].minus(takerFee), - ); - expect(newBalances[feeRecipientAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[feeRecipientAddress][zrxToken.address].add(makerFee.add(takerFee)), - ); - }); - - it('should throw if a signedOrder is expired', async () => { - const currentTimestamp = await getLatestBlockTimestampAsync(); - const signedOrder = await orderFactory.newSignedOrderAsync({ - expirationTimeSeconds: new BigNumber(currentTimestamp).sub(10), - }); - - return expectTransactionFailedAsync( - exchangeWrapper.fillOrKillOrderAsync(signedOrder, takerAddress), - RevertReason.OrderUnfillable, - ); - }); - - it('should throw if entire takerAssetFillAmount not filled', async () => { - const signedOrder = await orderFactory.newSignedOrderAsync(); - - await exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { - takerAssetFillAmount: signedOrder.takerAssetAmount.div(2), - }); - - return expectTransactionFailedAsync( - exchangeWrapper.fillOrKillOrderAsync(signedOrder, takerAddress), - RevertReason.CompleteFillFailed, - ); - }); - }); - - describe('fillOrderNoThrow', () => { - const reentrancyTest = (functionNames: string[]) => { - _.forEach(functionNames, async (functionName: string, functionId: number) => { - const description = `should not allow fillOrderNoThrow to reenter the Exchange contract via ${functionName}`; - it(description, async () => { - const signedOrder = await orderFactory.newSignedOrderAsync({ - makerAssetData: assetDataUtils.encodeERC20AssetData(reentrantErc20Token.address), - }); - await web3Wrapper.awaitTransactionSuccessAsync( - await reentrantErc20Token.setCurrentFunction.sendTransactionAsync(functionId), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - await exchangeWrapper.fillOrderNoThrowAsync(signedOrder, takerAddress); - const newBalances = await erc20Wrapper.getBalancesAsync(); - expect(erc20Balances).to.deep.equal(newBalances); - }); - }); - }; - describe('fillOrderNoThrow reentrancy tests', () => reentrancyTest(constants.FUNCTIONS_WITH_MUTEX)); - - it('should transfer the correct amounts', async () => { - const signedOrder = await orderFactory.newSignedOrderAsync({ - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(200), 18), - }); - const takerAssetFillAmount = signedOrder.takerAssetAmount.div(2); - - await exchangeWrapper.fillOrderNoThrowAsync(signedOrder, takerAddress, { - takerAssetFillAmount, - // HACK(albrow): We need to hardcode the gas estimate here because - // the Geth gas estimator doesn't work with the way we use - // delegatecall and swallow errors. - gas: 250000, - }); - - const newBalances = await erc20Wrapper.getBalancesAsync(); - const makerAssetFilledAmount = takerAssetFillAmount - .times(signedOrder.makerAssetAmount) - .dividedToIntegerBy(signedOrder.takerAssetAmount); - const makerFee = signedOrder.makerFee - .times(makerAssetFilledAmount) - .dividedToIntegerBy(signedOrder.makerAssetAmount); - const takerFee = signedOrder.takerFee - .times(makerAssetFilledAmount) - .dividedToIntegerBy(signedOrder.makerAssetAmount); - - expect(newBalances[makerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[makerAddress][defaultMakerAssetAddress].minus(makerAssetFilledAmount), - ); - expect(newBalances[makerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[makerAddress][defaultTakerAssetAddress].add(takerAssetFillAmount), - ); - expect(newBalances[makerAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[makerAddress][zrxToken.address].minus(makerFee), - ); - expect(newBalances[takerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[takerAddress][defaultTakerAssetAddress].minus(takerAssetFillAmount), - ); - expect(newBalances[takerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[takerAddress][defaultMakerAssetAddress].add(makerAssetFilledAmount), - ); - expect(newBalances[takerAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[takerAddress][zrxToken.address].minus(takerFee), - ); - expect(newBalances[feeRecipientAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[feeRecipientAddress][zrxToken.address].add(makerFee.add(takerFee)), - ); - }); - - it('should not change erc20Balances if maker erc20Balances are too low to fill order', async () => { - const signedOrder = await orderFactory.newSignedOrderAsync({ - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100000), 18), - }); - - await exchangeWrapper.fillOrderNoThrowAsync(signedOrder, takerAddress); - const newBalances = await erc20Wrapper.getBalancesAsync(); - expect(newBalances).to.be.deep.equal(erc20Balances); - }); - - it('should not change erc20Balances if taker erc20Balances are too low to fill order', async () => { - const signedOrder = await orderFactory.newSignedOrderAsync({ - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100000), 18), - }); - - await exchangeWrapper.fillOrderNoThrowAsync(signedOrder, takerAddress); - const newBalances = await erc20Wrapper.getBalancesAsync(); - expect(newBalances).to.be.deep.equal(erc20Balances); - }); - - it('should not change erc20Balances if maker allowances are too low to fill order', async () => { - const signedOrder = await orderFactory.newSignedOrderAsync(); - await web3Wrapper.awaitTransactionSuccessAsync( - await erc20TokenA.approve.sendTransactionAsync(erc20Proxy.address, new BigNumber(0), { - from: makerAddress, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - await exchangeWrapper.fillOrderNoThrowAsync(signedOrder, takerAddress); - await web3Wrapper.awaitTransactionSuccessAsync( - await erc20TokenA.approve.sendTransactionAsync(erc20Proxy.address, constants.INITIAL_ERC20_ALLOWANCE, { - from: makerAddress, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - - const newBalances = await erc20Wrapper.getBalancesAsync(); - expect(newBalances).to.be.deep.equal(erc20Balances); - }); - - it('should not change erc20Balances if taker allowances are too low to fill order', async () => { - const signedOrder = await orderFactory.newSignedOrderAsync(); - await web3Wrapper.awaitTransactionSuccessAsync( - await erc20TokenB.approve.sendTransactionAsync(erc20Proxy.address, new BigNumber(0), { - from: takerAddress, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - await exchangeWrapper.fillOrderNoThrowAsync(signedOrder, takerAddress); - await web3Wrapper.awaitTransactionSuccessAsync( - await erc20TokenB.approve.sendTransactionAsync(erc20Proxy.address, constants.INITIAL_ERC20_ALLOWANCE, { - from: takerAddress, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - - const newBalances = await erc20Wrapper.getBalancesAsync(); - expect(newBalances).to.be.deep.equal(erc20Balances); - }); - - it('should not change erc20Balances if makerAssetAddress is ZRX, makerAssetAmount + makerFee > maker balance', async () => { - const makerZRXBalance = new BigNumber(erc20Balances[makerAddress][zrxToken.address]); - const signedOrder = await orderFactory.newSignedOrderAsync({ - makerAssetAmount: makerZRXBalance, - makerFee: new BigNumber(1), - makerAssetData: assetDataUtils.encodeERC20AssetData(zrxToken.address), - }); - await exchangeWrapper.fillOrderNoThrowAsync(signedOrder, takerAddress); - const newBalances = await erc20Wrapper.getBalancesAsync(); - expect(newBalances).to.be.deep.equal(erc20Balances); - }); - - it('should not change erc20Balances if makerAssetAddress is ZRX, makerAssetAmount + makerFee > maker allowance', async () => { - const makerZRXAllowance = await zrxToken.allowance.callAsync(makerAddress, erc20Proxy.address); - const signedOrder = await orderFactory.newSignedOrderAsync({ - makerAssetAmount: new BigNumber(makerZRXAllowance), - makerFee: new BigNumber(1), - makerAssetData: assetDataUtils.encodeERC20AssetData(zrxToken.address), - }); - await exchangeWrapper.fillOrderNoThrowAsync(signedOrder, takerAddress); - const newBalances = await erc20Wrapper.getBalancesAsync(); - expect(newBalances).to.be.deep.equal(erc20Balances); - }); - - it('should not change erc20Balances if takerAssetAddress is ZRX, takerAssetAmount + takerFee > taker balance', async () => { - const takerZRXBalance = new BigNumber(erc20Balances[takerAddress][zrxToken.address]); - const signedOrder = await orderFactory.newSignedOrderAsync({ - takerAssetAmount: takerZRXBalance, - takerFee: new BigNumber(1), - takerAssetData: assetDataUtils.encodeERC20AssetData(zrxToken.address), - }); - await exchangeWrapper.fillOrderNoThrowAsync(signedOrder, takerAddress); - const newBalances = await erc20Wrapper.getBalancesAsync(); - expect(newBalances).to.be.deep.equal(erc20Balances); - }); - - it('should not change erc20Balances if takerAssetAddress is ZRX, takerAssetAmount + takerFee > taker allowance', async () => { - const takerZRXAllowance = await zrxToken.allowance.callAsync(takerAddress, erc20Proxy.address); - const signedOrder = await orderFactory.newSignedOrderAsync({ - takerAssetAmount: new BigNumber(takerZRXAllowance), - takerFee: new BigNumber(1), - takerAssetData: assetDataUtils.encodeERC20AssetData(zrxToken.address), - }); - await exchangeWrapper.fillOrderNoThrowAsync(signedOrder, takerAddress); - const newBalances = await erc20Wrapper.getBalancesAsync(); - expect(newBalances).to.be.deep.equal(erc20Balances); - }); - - it('should successfully exchange ERC721 tokens', async () => { - // Construct Exchange parameters - const makerAssetId = erc721MakerAssetId; - const takerAssetId = erc721TakerAssetId; - const signedOrder = await orderFactory.newSignedOrderAsync({ - makerAssetAmount: new BigNumber(1), - takerAssetAmount: new BigNumber(1), - makerAssetData: assetDataUtils.encodeERC721AssetData(erc721Token.address, makerAssetId), - takerAssetData: assetDataUtils.encodeERC721AssetData(erc721Token.address, takerAssetId), - }); - // Verify pre-conditions - const initialOwnerMakerAsset = await erc721Token.ownerOf.callAsync(makerAssetId); - expect(initialOwnerMakerAsset).to.be.bignumber.equal(makerAddress); - const initialOwnerTakerAsset = await erc721Token.ownerOf.callAsync(takerAssetId); - expect(initialOwnerTakerAsset).to.be.bignumber.equal(takerAddress); - // Call Exchange - const takerAssetFillAmount = signedOrder.takerAssetAmount; - await exchangeWrapper.fillOrderNoThrowAsync(signedOrder, takerAddress, { - takerAssetFillAmount, - // HACK(albrow): We need to hardcode the gas estimate here because - // the Geth gas estimator doesn't work with the way we use - // delegatecall and swallow errors. - gas: 280000, - }); - // Verify post-conditions - const newOwnerMakerAsset = await erc721Token.ownerOf.callAsync(makerAssetId); - expect(newOwnerMakerAsset).to.be.bignumber.equal(takerAddress); - const newOwnerTakerAsset = await erc721Token.ownerOf.callAsync(takerAssetId); - expect(newOwnerTakerAsset).to.be.bignumber.equal(makerAddress); - }); - }); - - describe('batch functions', () => { - let signedOrders: SignedOrder[]; - beforeEach(async () => { - signedOrders = [ - await orderFactory.newSignedOrderAsync(), - await orderFactory.newSignedOrderAsync(), - await orderFactory.newSignedOrderAsync(), - ]; - }); - - describe('batchFillOrders', () => { - const reentrancyTest = (functionNames: string[]) => { - _.forEach(functionNames, async (functionName: string, functionId: number) => { - const description = `should not allow batchFillOrders to reenter the Exchange contract via ${functionName}`; - it(description, async () => { - const signedOrder = await orderFactory.newSignedOrderAsync({ - makerAssetData: assetDataUtils.encodeERC20AssetData(reentrantErc20Token.address), - }); - await web3Wrapper.awaitTransactionSuccessAsync( - await reentrantErc20Token.setCurrentFunction.sendTransactionAsync(functionId), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - await expectTransactionFailedAsync( - exchangeWrapper.batchFillOrdersAsync([signedOrder], takerAddress), - RevertReason.TransferFailed, - ); - }); - }); - }; - describe('batchFillOrders reentrancy tests', () => reentrancyTest(constants.FUNCTIONS_WITH_MUTEX)); - - it('should transfer the correct amounts', async () => { - const takerAssetFillAmounts: BigNumber[] = []; - const makerAssetAddress = erc20TokenA.address; - const takerAssetAddress = erc20TokenB.address; - _.forEach(signedOrders, signedOrder => { - const takerAssetFillAmount = signedOrder.takerAssetAmount.div(2); - const makerAssetFilledAmount = takerAssetFillAmount - .times(signedOrder.makerAssetAmount) - .dividedToIntegerBy(signedOrder.takerAssetAmount); - const makerFee = signedOrder.makerFee - .times(makerAssetFilledAmount) - .dividedToIntegerBy(signedOrder.makerAssetAmount); - const takerFee = signedOrder.takerFee - .times(makerAssetFilledAmount) - .dividedToIntegerBy(signedOrder.makerAssetAmount); - takerAssetFillAmounts.push(takerAssetFillAmount); - erc20Balances[makerAddress][makerAssetAddress] = erc20Balances[makerAddress][ - makerAssetAddress - ].minus(makerAssetFilledAmount); - erc20Balances[makerAddress][takerAssetAddress] = erc20Balances[makerAddress][takerAssetAddress].add( - takerAssetFillAmount, - ); - erc20Balances[makerAddress][zrxToken.address] = erc20Balances[makerAddress][zrxToken.address].minus( - makerFee, - ); - erc20Balances[takerAddress][makerAssetAddress] = erc20Balances[takerAddress][makerAssetAddress].add( - makerAssetFilledAmount, - ); - erc20Balances[takerAddress][takerAssetAddress] = erc20Balances[takerAddress][ - takerAssetAddress - ].minus(takerAssetFillAmount); - erc20Balances[takerAddress][zrxToken.address] = erc20Balances[takerAddress][zrxToken.address].minus( - takerFee, - ); - erc20Balances[feeRecipientAddress][zrxToken.address] = erc20Balances[feeRecipientAddress][ - zrxToken.address - ].add(makerFee.add(takerFee)); - }); - - await exchangeWrapper.batchFillOrdersAsync(signedOrders, takerAddress, { - takerAssetFillAmounts, - }); - - const newBalances = await erc20Wrapper.getBalancesAsync(); - expect(newBalances).to.be.deep.equal(erc20Balances); - }); - }); - - describe('batchFillOrKillOrders', () => { - const reentrancyTest = (functionNames: string[]) => { - _.forEach(functionNames, async (functionName: string, functionId: number) => { - const description = `should not allow batchFillOrKillOrders to reenter the Exchange contract via ${functionName}`; - it(description, async () => { - const signedOrder = await orderFactory.newSignedOrderAsync({ - makerAssetData: assetDataUtils.encodeERC20AssetData(reentrantErc20Token.address), - }); - await web3Wrapper.awaitTransactionSuccessAsync( - await reentrantErc20Token.setCurrentFunction.sendTransactionAsync(functionId), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - await expectTransactionFailedAsync( - exchangeWrapper.batchFillOrKillOrdersAsync([signedOrder], takerAddress), - RevertReason.TransferFailed, - ); - }); - }); - }; - describe('batchFillOrKillOrders reentrancy tests', () => reentrancyTest(constants.FUNCTIONS_WITH_MUTEX)); - - it('should transfer the correct amounts', async () => { - const takerAssetFillAmounts: BigNumber[] = []; - const makerAssetAddress = erc20TokenA.address; - const takerAssetAddress = erc20TokenB.address; - _.forEach(signedOrders, signedOrder => { - const takerAssetFillAmount = signedOrder.takerAssetAmount.div(2); - const makerAssetFilledAmount = takerAssetFillAmount - .times(signedOrder.makerAssetAmount) - .dividedToIntegerBy(signedOrder.takerAssetAmount); - const makerFee = signedOrder.makerFee - .times(makerAssetFilledAmount) - .dividedToIntegerBy(signedOrder.makerAssetAmount); - const takerFee = signedOrder.takerFee - .times(makerAssetFilledAmount) - .dividedToIntegerBy(signedOrder.makerAssetAmount); - takerAssetFillAmounts.push(takerAssetFillAmount); - erc20Balances[makerAddress][makerAssetAddress] = erc20Balances[makerAddress][ - makerAssetAddress - ].minus(makerAssetFilledAmount); - erc20Balances[makerAddress][takerAssetAddress] = erc20Balances[makerAddress][takerAssetAddress].add( - takerAssetFillAmount, - ); - erc20Balances[makerAddress][zrxToken.address] = erc20Balances[makerAddress][zrxToken.address].minus( - makerFee, - ); - erc20Balances[takerAddress][makerAssetAddress] = erc20Balances[takerAddress][makerAssetAddress].add( - makerAssetFilledAmount, - ); - erc20Balances[takerAddress][takerAssetAddress] = erc20Balances[takerAddress][ - takerAssetAddress - ].minus(takerAssetFillAmount); - erc20Balances[takerAddress][zrxToken.address] = erc20Balances[takerAddress][zrxToken.address].minus( - takerFee, - ); - erc20Balances[feeRecipientAddress][zrxToken.address] = erc20Balances[feeRecipientAddress][ - zrxToken.address - ].add(makerFee.add(takerFee)); - }); - - await exchangeWrapper.batchFillOrKillOrdersAsync(signedOrders, takerAddress, { - takerAssetFillAmounts, - }); - - const newBalances = await erc20Wrapper.getBalancesAsync(); - expect(newBalances).to.be.deep.equal(erc20Balances); - }); - - it('should throw if a single signedOrder does not fill the expected amount', async () => { - const takerAssetFillAmounts: BigNumber[] = []; - _.forEach(signedOrders, signedOrder => { - const takerAssetFillAmount = signedOrder.takerAssetAmount.div(2); - takerAssetFillAmounts.push(takerAssetFillAmount); - }); - - await exchangeWrapper.fillOrKillOrderAsync(signedOrders[0], takerAddress); - - return expectTransactionFailedAsync( - exchangeWrapper.batchFillOrKillOrdersAsync(signedOrders, takerAddress, { - takerAssetFillAmounts, - }), - RevertReason.OrderUnfillable, - ); - }); - }); - - describe('batchFillOrdersNoThrow', async () => { - const reentrancyTest = (functionNames: string[]) => { - _.forEach(functionNames, async (functionName: string, functionId: number) => { - const description = `should not allow batchFillOrdersNoThrow to reenter the Exchange contract via ${functionName}`; - it(description, async () => { - const signedOrder = await orderFactory.newSignedOrderAsync({ - makerAssetData: assetDataUtils.encodeERC20AssetData(reentrantErc20Token.address), - }); - await web3Wrapper.awaitTransactionSuccessAsync( - await reentrantErc20Token.setCurrentFunction.sendTransactionAsync(functionId), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - await exchangeWrapper.batchFillOrdersNoThrowAsync([signedOrder], takerAddress); - const newBalances = await erc20Wrapper.getBalancesAsync(); - expect(erc20Balances).to.deep.equal(newBalances); - }); - }); - }; - describe('batchFillOrdersNoThrow reentrancy tests', () => reentrancyTest(constants.FUNCTIONS_WITH_MUTEX)); - - it('should transfer the correct amounts', async () => { - const takerAssetFillAmounts: BigNumber[] = []; - const makerAssetAddress = erc20TokenA.address; - const takerAssetAddress = erc20TokenB.address; - _.forEach(signedOrders, signedOrder => { - const takerAssetFillAmount = signedOrder.takerAssetAmount.div(2); - const makerAssetFilledAmount = takerAssetFillAmount - .times(signedOrder.makerAssetAmount) - .dividedToIntegerBy(signedOrder.takerAssetAmount); - const makerFee = signedOrder.makerFee - .times(makerAssetFilledAmount) - .dividedToIntegerBy(signedOrder.makerAssetAmount); - const takerFee = signedOrder.takerFee - .times(makerAssetFilledAmount) - .dividedToIntegerBy(signedOrder.makerAssetAmount); - takerAssetFillAmounts.push(takerAssetFillAmount); - erc20Balances[makerAddress][makerAssetAddress] = erc20Balances[makerAddress][ - makerAssetAddress - ].minus(makerAssetFilledAmount); - erc20Balances[makerAddress][takerAssetAddress] = erc20Balances[makerAddress][takerAssetAddress].add( - takerAssetFillAmount, - ); - erc20Balances[makerAddress][zrxToken.address] = erc20Balances[makerAddress][zrxToken.address].minus( - makerFee, - ); - erc20Balances[takerAddress][makerAssetAddress] = erc20Balances[takerAddress][makerAssetAddress].add( - makerAssetFilledAmount, - ); - erc20Balances[takerAddress][takerAssetAddress] = erc20Balances[takerAddress][ - takerAssetAddress - ].minus(takerAssetFillAmount); - erc20Balances[takerAddress][zrxToken.address] = erc20Balances[takerAddress][zrxToken.address].minus( - takerFee, - ); - erc20Balances[feeRecipientAddress][zrxToken.address] = erc20Balances[feeRecipientAddress][ - zrxToken.address - ].add(makerFee.add(takerFee)); - }); - - await exchangeWrapper.batchFillOrdersNoThrowAsync(signedOrders, takerAddress, { - takerAssetFillAmounts, - // HACK(albrow): We need to hardcode the gas estimate here because - // the Geth gas estimator doesn't work with the way we use - // delegatecall and swallow errors. - gas: 600000, - }); - - const newBalances = await erc20Wrapper.getBalancesAsync(); - expect(newBalances).to.be.deep.equal(erc20Balances); - }); - - it('should not throw if an order is invalid and fill the remaining orders', async () => { - const takerAssetFillAmounts: BigNumber[] = []; - const makerAssetAddress = erc20TokenA.address; - const takerAssetAddress = erc20TokenB.address; - - const invalidOrder = { - ...signedOrders[0], - signature: '0x00', - }; - const validOrders = signedOrders.slice(1); - - takerAssetFillAmounts.push(invalidOrder.takerAssetAmount.div(2)); - _.forEach(validOrders, signedOrder => { - const takerAssetFillAmount = signedOrder.takerAssetAmount.div(2); - const makerAssetFilledAmount = takerAssetFillAmount - .times(signedOrder.makerAssetAmount) - .dividedToIntegerBy(signedOrder.takerAssetAmount); - const makerFee = signedOrder.makerFee - .times(makerAssetFilledAmount) - .dividedToIntegerBy(signedOrder.makerAssetAmount); - const takerFee = signedOrder.takerFee - .times(makerAssetFilledAmount) - .dividedToIntegerBy(signedOrder.makerAssetAmount); - takerAssetFillAmounts.push(takerAssetFillAmount); - erc20Balances[makerAddress][makerAssetAddress] = erc20Balances[makerAddress][ - makerAssetAddress - ].minus(makerAssetFilledAmount); - erc20Balances[makerAddress][takerAssetAddress] = erc20Balances[makerAddress][takerAssetAddress].add( - takerAssetFillAmount, - ); - erc20Balances[makerAddress][zrxToken.address] = erc20Balances[makerAddress][zrxToken.address].minus( - makerFee, - ); - erc20Balances[takerAddress][makerAssetAddress] = erc20Balances[takerAddress][makerAssetAddress].add( - makerAssetFilledAmount, - ); - erc20Balances[takerAddress][takerAssetAddress] = erc20Balances[takerAddress][ - takerAssetAddress - ].minus(takerAssetFillAmount); - erc20Balances[takerAddress][zrxToken.address] = erc20Balances[takerAddress][zrxToken.address].minus( - takerFee, - ); - erc20Balances[feeRecipientAddress][zrxToken.address] = erc20Balances[feeRecipientAddress][ - zrxToken.address - ].add(makerFee.add(takerFee)); - }); - - const newOrders = [invalidOrder, ...validOrders]; - await exchangeWrapper.batchFillOrdersNoThrowAsync(newOrders, takerAddress, { - takerAssetFillAmounts, - // HACK(albrow): We need to hardcode the gas estimate here because - // the Geth gas estimator doesn't work with the way we use - // delegatecall and swallow errors. - gas: 450000, - }); - - const newBalances = await erc20Wrapper.getBalancesAsync(); - expect(newBalances).to.be.deep.equal(erc20Balances); - }); - }); - - describe('marketSellOrders', () => { - const reentrancyTest = (functionNames: string[]) => { - _.forEach(functionNames, async (functionName: string, functionId: number) => { - const description = `should not allow marketSellOrders to reenter the Exchange contract via ${functionName}`; - it(description, async () => { - const signedOrder = await orderFactory.newSignedOrderAsync({ - makerAssetData: assetDataUtils.encodeERC20AssetData(reentrantErc20Token.address), - }); - await web3Wrapper.awaitTransactionSuccessAsync( - await reentrantErc20Token.setCurrentFunction.sendTransactionAsync(functionId), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - await expectTransactionFailedAsync( - exchangeWrapper.marketSellOrdersAsync([signedOrder], takerAddress, { - takerAssetFillAmount: signedOrder.takerAssetAmount, - }), - RevertReason.TransferFailed, - ); - }); - }); - }; - describe('marketSellOrders reentrancy tests', () => reentrancyTest(constants.FUNCTIONS_WITH_MUTEX)); - - it('should stop when the entire takerAssetFillAmount is filled', async () => { - const takerAssetFillAmount = signedOrders[0].takerAssetAmount.plus( - signedOrders[1].takerAssetAmount.div(2), - ); - await exchangeWrapper.marketSellOrdersAsync(signedOrders, takerAddress, { - takerAssetFillAmount, - }); - - const newBalances = await erc20Wrapper.getBalancesAsync(); - - const makerAssetFilledAmount = signedOrders[0].makerAssetAmount.add( - signedOrders[1].makerAssetAmount.dividedToIntegerBy(2), - ); - const makerFee = signedOrders[0].makerFee.add(signedOrders[1].makerFee.dividedToIntegerBy(2)); - const takerFee = signedOrders[0].takerFee.add(signedOrders[1].takerFee.dividedToIntegerBy(2)); - expect(newBalances[makerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[makerAddress][defaultMakerAssetAddress].minus(makerAssetFilledAmount), - ); - expect(newBalances[makerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[makerAddress][defaultTakerAssetAddress].add(takerAssetFillAmount), - ); - expect(newBalances[makerAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[makerAddress][zrxToken.address].minus(makerFee), - ); - expect(newBalances[takerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[takerAddress][defaultTakerAssetAddress].minus(takerAssetFillAmount), - ); - expect(newBalances[takerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[takerAddress][defaultMakerAssetAddress].add(makerAssetFilledAmount), - ); - expect(newBalances[takerAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[takerAddress][zrxToken.address].minus(takerFee), - ); - expect(newBalances[feeRecipientAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[feeRecipientAddress][zrxToken.address].add(makerFee.add(takerFee)), - ); - }); - - it('should fill all signedOrders if cannot fill entire takerAssetFillAmount', async () => { - const takerAssetFillAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(100000), 18); - _.forEach(signedOrders, signedOrder => { - erc20Balances[makerAddress][defaultMakerAssetAddress] = erc20Balances[makerAddress][ - defaultMakerAssetAddress - ].minus(signedOrder.makerAssetAmount); - erc20Balances[makerAddress][defaultTakerAssetAddress] = erc20Balances[makerAddress][ - defaultTakerAssetAddress - ].add(signedOrder.takerAssetAmount); - erc20Balances[makerAddress][zrxToken.address] = erc20Balances[makerAddress][zrxToken.address].minus( - signedOrder.makerFee, - ); - erc20Balances[takerAddress][defaultMakerAssetAddress] = erc20Balances[takerAddress][ - defaultMakerAssetAddress - ].add(signedOrder.makerAssetAmount); - erc20Balances[takerAddress][defaultTakerAssetAddress] = erc20Balances[takerAddress][ - defaultTakerAssetAddress - ].minus(signedOrder.takerAssetAmount); - erc20Balances[takerAddress][zrxToken.address] = erc20Balances[takerAddress][zrxToken.address].minus( - signedOrder.takerFee, - ); - erc20Balances[feeRecipientAddress][zrxToken.address] = erc20Balances[feeRecipientAddress][ - zrxToken.address - ].add(signedOrder.makerFee.add(signedOrder.takerFee)); - }); - await exchangeWrapper.marketSellOrdersAsync(signedOrders, takerAddress, { - takerAssetFillAmount, - }); - - const newBalances = await erc20Wrapper.getBalancesAsync(); - expect(newBalances).to.be.deep.equal(erc20Balances); - }); - - it('should throw when a signedOrder does not use the same takerAssetAddress', async () => { - signedOrders = [ - await orderFactory.newSignedOrderAsync(), - await orderFactory.newSignedOrderAsync({ - takerAssetData: assetDataUtils.encodeERC20AssetData(zrxToken.address), - }), - await orderFactory.newSignedOrderAsync(), - ]; - - return expectTransactionFailedAsync( - exchangeWrapper.marketSellOrdersAsync(signedOrders, takerAddress, { - takerAssetFillAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(1000), 18), - }), - // We simply use the takerAssetData from the first order for all orders. - // If they are not the same, the contract throws when validating the order signature - RevertReason.InvalidOrderSignature, - ); - }); - }); - - describe('marketSellOrdersNoThrow', () => { - const reentrancyTest = (functionNames: string[]) => { - _.forEach(functionNames, async (functionName: string, functionId: number) => { - const description = `should not allow marketSellOrdersNoThrow to reenter the Exchange contract via ${functionName}`; - it(description, async () => { - const signedOrder = await orderFactory.newSignedOrderAsync({ - makerAssetData: assetDataUtils.encodeERC20AssetData(reentrantErc20Token.address), - }); - await web3Wrapper.awaitTransactionSuccessAsync( - await reentrantErc20Token.setCurrentFunction.sendTransactionAsync(functionId), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - await exchangeWrapper.marketSellOrdersNoThrowAsync([signedOrder], takerAddress, { - takerAssetFillAmount: signedOrder.takerAssetAmount, - }); - const newBalances = await erc20Wrapper.getBalancesAsync(); - expect(erc20Balances).to.deep.equal(newBalances); - }); - }); - }; - describe('marketSellOrdersNoThrow reentrancy tests', () => reentrancyTest(constants.FUNCTIONS_WITH_MUTEX)); - - it('should stop when the entire takerAssetFillAmount is filled', async () => { - const takerAssetFillAmount = signedOrders[0].takerAssetAmount.plus( - signedOrders[1].takerAssetAmount.div(2), - ); - await exchangeWrapper.marketSellOrdersNoThrowAsync(signedOrders, takerAddress, { - takerAssetFillAmount, - // HACK(albrow): We need to hardcode the gas estimate here because - // the Geth gas estimator doesn't work with the way we use - // delegatecall and swallow errors. - gas: 6000000, - }); - - const newBalances = await erc20Wrapper.getBalancesAsync(); - - const makerAssetFilledAmount = signedOrders[0].makerAssetAmount.add( - signedOrders[1].makerAssetAmount.dividedToIntegerBy(2), - ); - const makerFee = signedOrders[0].makerFee.add(signedOrders[1].makerFee.dividedToIntegerBy(2)); - const takerFee = signedOrders[0].takerFee.add(signedOrders[1].takerFee.dividedToIntegerBy(2)); - expect(newBalances[makerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[makerAddress][defaultMakerAssetAddress].minus(makerAssetFilledAmount), - ); - expect(newBalances[makerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[makerAddress][defaultTakerAssetAddress].add(takerAssetFillAmount), - ); - expect(newBalances[makerAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[makerAddress][zrxToken.address].minus(makerFee), - ); - expect(newBalances[takerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[takerAddress][defaultTakerAssetAddress].minus(takerAssetFillAmount), - ); - expect(newBalances[takerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[takerAddress][defaultMakerAssetAddress].add(makerAssetFilledAmount), - ); - expect(newBalances[takerAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[takerAddress][zrxToken.address].minus(takerFee), - ); - expect(newBalances[feeRecipientAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[feeRecipientAddress][zrxToken.address].add(makerFee.add(takerFee)), - ); - }); - - it('should fill all signedOrders if cannot fill entire takerAssetFillAmount', async () => { - const takerAssetFillAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(100000), 18); - _.forEach(signedOrders, signedOrder => { - erc20Balances[makerAddress][defaultMakerAssetAddress] = erc20Balances[makerAddress][ - defaultMakerAssetAddress - ].minus(signedOrder.makerAssetAmount); - erc20Balances[makerAddress][defaultTakerAssetAddress] = erc20Balances[makerAddress][ - defaultTakerAssetAddress - ].add(signedOrder.takerAssetAmount); - erc20Balances[makerAddress][zrxToken.address] = erc20Balances[makerAddress][zrxToken.address].minus( - signedOrder.makerFee, - ); - erc20Balances[takerAddress][defaultMakerAssetAddress] = erc20Balances[takerAddress][ - defaultMakerAssetAddress - ].add(signedOrder.makerAssetAmount); - erc20Balances[takerAddress][defaultTakerAssetAddress] = erc20Balances[takerAddress][ - defaultTakerAssetAddress - ].minus(signedOrder.takerAssetAmount); - erc20Balances[takerAddress][zrxToken.address] = erc20Balances[takerAddress][zrxToken.address].minus( - signedOrder.takerFee, - ); - erc20Balances[feeRecipientAddress][zrxToken.address] = erc20Balances[feeRecipientAddress][ - zrxToken.address - ].add(signedOrder.makerFee.add(signedOrder.takerFee)); - }); - await exchangeWrapper.marketSellOrdersNoThrowAsync(signedOrders, takerAddress, { - takerAssetFillAmount, - // HACK(albrow): We need to hardcode the gas estimate here because - // the Geth gas estimator doesn't work with the way we use - // delegatecall and swallow errors. - gas: 600000, - }); - - const newBalances = await erc20Wrapper.getBalancesAsync(); - expect(newBalances).to.be.deep.equal(erc20Balances); - }); - - it('should not fill a signedOrder that does not use the same takerAssetAddress', async () => { - signedOrders = [ - await orderFactory.newSignedOrderAsync(), - await orderFactory.newSignedOrderAsync(), - await orderFactory.newSignedOrderAsync({ - takerAssetData: assetDataUtils.encodeERC20AssetData(zrxToken.address), - }), - ]; - const takerAssetFillAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(100000), 18); - const filledSignedOrders = signedOrders.slice(0, -1); - _.forEach(filledSignedOrders, signedOrder => { - erc20Balances[makerAddress][defaultMakerAssetAddress] = erc20Balances[makerAddress][ - defaultMakerAssetAddress - ].minus(signedOrder.makerAssetAmount); - erc20Balances[makerAddress][defaultTakerAssetAddress] = erc20Balances[makerAddress][ - defaultTakerAssetAddress - ].add(signedOrder.takerAssetAmount); - erc20Balances[makerAddress][zrxToken.address] = erc20Balances[makerAddress][zrxToken.address].minus( - signedOrder.makerFee, - ); - erc20Balances[takerAddress][defaultMakerAssetAddress] = erc20Balances[takerAddress][ - defaultMakerAssetAddress - ].add(signedOrder.makerAssetAmount); - erc20Balances[takerAddress][defaultTakerAssetAddress] = erc20Balances[takerAddress][ - defaultTakerAssetAddress - ].minus(signedOrder.takerAssetAmount); - erc20Balances[takerAddress][zrxToken.address] = erc20Balances[takerAddress][zrxToken.address].minus( - signedOrder.takerFee, - ); - erc20Balances[feeRecipientAddress][zrxToken.address] = erc20Balances[feeRecipientAddress][ - zrxToken.address - ].add(signedOrder.makerFee.add(signedOrder.takerFee)); - }); - await exchangeWrapper.marketSellOrdersNoThrowAsync(signedOrders, takerAddress, { - takerAssetFillAmount, - // HACK(albrow): We need to hardcode the gas estimate here because - // the Geth gas estimator doesn't work with the way we use - // delegatecall and swallow errors. - gas: 600000, - }); - - const newBalances = await erc20Wrapper.getBalancesAsync(); - expect(newBalances).to.be.deep.equal(erc20Balances); - }); - }); - - describe('marketBuyOrders', () => { - const reentrancyTest = (functionNames: string[]) => { - _.forEach(functionNames, async (functionName: string, functionId: number) => { - const description = `should not allow marketBuyOrders to reenter the Exchange contract via ${functionName}`; - it(description, async () => { - const signedOrder = await orderFactory.newSignedOrderAsync({ - makerAssetData: assetDataUtils.encodeERC20AssetData(reentrantErc20Token.address), - }); - await web3Wrapper.awaitTransactionSuccessAsync( - await reentrantErc20Token.setCurrentFunction.sendTransactionAsync(functionId), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - await expectTransactionFailedAsync( - exchangeWrapper.marketBuyOrdersAsync([signedOrder], takerAddress, { - makerAssetFillAmount: signedOrder.makerAssetAmount, - }), - RevertReason.TransferFailed, - ); - }); - }); - }; - describe('marketBuyOrders reentrancy tests', () => reentrancyTest(constants.FUNCTIONS_WITH_MUTEX)); - - it('should stop when the entire makerAssetFillAmount is filled', async () => { - const makerAssetFillAmount = signedOrders[0].makerAssetAmount.plus( - signedOrders[1].makerAssetAmount.div(2), - ); - await exchangeWrapper.marketBuyOrdersAsync(signedOrders, takerAddress, { - makerAssetFillAmount, - }); - - const newBalances = await erc20Wrapper.getBalancesAsync(); - - const makerAmountBought = signedOrders[0].takerAssetAmount.add( - signedOrders[1].takerAssetAmount.dividedToIntegerBy(2), - ); - const makerFee = signedOrders[0].makerFee.add(signedOrders[1].makerFee.dividedToIntegerBy(2)); - const takerFee = signedOrders[0].takerFee.add(signedOrders[1].takerFee.dividedToIntegerBy(2)); - expect(newBalances[makerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[makerAddress][defaultMakerAssetAddress].minus(makerAssetFillAmount), - ); - expect(newBalances[makerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[makerAddress][defaultTakerAssetAddress].add(makerAmountBought), - ); - expect(newBalances[makerAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[makerAddress][zrxToken.address].minus(makerFee), - ); - expect(newBalances[takerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[takerAddress][defaultTakerAssetAddress].minus(makerAmountBought), - ); - expect(newBalances[takerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[takerAddress][defaultMakerAssetAddress].add(makerAssetFillAmount), - ); - expect(newBalances[takerAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[takerAddress][zrxToken.address].minus(takerFee), - ); - expect(newBalances[feeRecipientAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[feeRecipientAddress][zrxToken.address].add(makerFee.add(takerFee)), - ); - }); - - it('should fill all signedOrders if cannot fill entire makerAssetFillAmount', async () => { - const makerAssetFillAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(100000), 18); - _.forEach(signedOrders, signedOrder => { - erc20Balances[makerAddress][defaultMakerAssetAddress] = erc20Balances[makerAddress][ - defaultMakerAssetAddress - ].minus(signedOrder.makerAssetAmount); - erc20Balances[makerAddress][defaultTakerAssetAddress] = erc20Balances[makerAddress][ - defaultTakerAssetAddress - ].add(signedOrder.takerAssetAmount); - erc20Balances[makerAddress][zrxToken.address] = erc20Balances[makerAddress][zrxToken.address].minus( - signedOrder.makerFee, - ); - erc20Balances[takerAddress][defaultMakerAssetAddress] = erc20Balances[takerAddress][ - defaultMakerAssetAddress - ].add(signedOrder.makerAssetAmount); - erc20Balances[takerAddress][defaultTakerAssetAddress] = erc20Balances[takerAddress][ - defaultTakerAssetAddress - ].minus(signedOrder.takerAssetAmount); - erc20Balances[takerAddress][zrxToken.address] = erc20Balances[takerAddress][zrxToken.address].minus( - signedOrder.takerFee, - ); - erc20Balances[feeRecipientAddress][zrxToken.address] = erc20Balances[feeRecipientAddress][ - zrxToken.address - ].add(signedOrder.makerFee.add(signedOrder.takerFee)); - }); - await exchangeWrapper.marketBuyOrdersAsync(signedOrders, takerAddress, { - makerAssetFillAmount, - }); - - const newBalances = await erc20Wrapper.getBalancesAsync(); - expect(newBalances).to.be.deep.equal(erc20Balances); - }); - - it('should throw when a signedOrder does not use the same makerAssetAddress', async () => { - signedOrders = [ - await orderFactory.newSignedOrderAsync(), - await orderFactory.newSignedOrderAsync({ - makerAssetData: assetDataUtils.encodeERC20AssetData(zrxToken.address), - }), - await orderFactory.newSignedOrderAsync(), - ]; - - return expectTransactionFailedAsync( - exchangeWrapper.marketBuyOrdersAsync(signedOrders, takerAddress, { - makerAssetFillAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(1000), 18), - }), - RevertReason.InvalidOrderSignature, - ); - }); - }); - - describe('marketBuyOrdersNoThrow', () => { - const reentrancyTest = (functionNames: string[]) => { - _.forEach(functionNames, async (functionName: string, functionId: number) => { - const description = `should not allow marketBuyOrdersNoThrow to reenter the Exchange contract via ${functionName}`; - it(description, async () => { - const signedOrder = await orderFactory.newSignedOrderAsync({ - makerAssetData: assetDataUtils.encodeERC20AssetData(reentrantErc20Token.address), - }); - await web3Wrapper.awaitTransactionSuccessAsync( - await reentrantErc20Token.setCurrentFunction.sendTransactionAsync(functionId), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - await exchangeWrapper.marketBuyOrdersNoThrowAsync([signedOrder], takerAddress, { - makerAssetFillAmount: signedOrder.makerAssetAmount, - }); - const newBalances = await erc20Wrapper.getBalancesAsync(); - expect(erc20Balances).to.deep.equal(newBalances); - }); - }); - }; - describe('marketBuyOrdersNoThrow reentrancy tests', () => reentrancyTest(constants.FUNCTIONS_WITH_MUTEX)); - - it('should stop when the entire makerAssetFillAmount is filled', async () => { - const makerAssetFillAmount = signedOrders[0].makerAssetAmount.plus( - signedOrders[1].makerAssetAmount.div(2), - ); - await exchangeWrapper.marketBuyOrdersNoThrowAsync(signedOrders, takerAddress, { - makerAssetFillAmount, - // HACK(albrow): We need to hardcode the gas estimate here because - // the Geth gas estimator doesn't work with the way we use - // delegatecall and swallow errors. - gas: 600000, - }); - - const newBalances = await erc20Wrapper.getBalancesAsync(); - - const makerAmountBought = signedOrders[0].takerAssetAmount.add( - signedOrders[1].takerAssetAmount.dividedToIntegerBy(2), - ); - const makerFee = signedOrders[0].makerFee.add(signedOrders[1].makerFee.dividedToIntegerBy(2)); - const takerFee = signedOrders[0].takerFee.add(signedOrders[1].takerFee.dividedToIntegerBy(2)); - expect(newBalances[makerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[makerAddress][defaultMakerAssetAddress].minus(makerAssetFillAmount), - ); - expect(newBalances[makerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[makerAddress][defaultTakerAssetAddress].add(makerAmountBought), - ); - expect(newBalances[makerAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[makerAddress][zrxToken.address].minus(makerFee), - ); - expect(newBalances[takerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[takerAddress][defaultTakerAssetAddress].minus(makerAmountBought), - ); - expect(newBalances[takerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[takerAddress][defaultMakerAssetAddress].add(makerAssetFillAmount), - ); - expect(newBalances[takerAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[takerAddress][zrxToken.address].minus(takerFee), - ); - expect(newBalances[feeRecipientAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[feeRecipientAddress][zrxToken.address].add(makerFee.add(takerFee)), - ); - }); - - it('should fill all signedOrders if cannot fill entire makerAssetFillAmount', async () => { - const makerAssetFillAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(100000), 18); - _.forEach(signedOrders, signedOrder => { - erc20Balances[makerAddress][defaultMakerAssetAddress] = erc20Balances[makerAddress][ - defaultMakerAssetAddress - ].minus(signedOrder.makerAssetAmount); - erc20Balances[makerAddress][defaultTakerAssetAddress] = erc20Balances[makerAddress][ - defaultTakerAssetAddress - ].add(signedOrder.takerAssetAmount); - erc20Balances[makerAddress][zrxToken.address] = erc20Balances[makerAddress][zrxToken.address].minus( - signedOrder.makerFee, - ); - erc20Balances[takerAddress][defaultMakerAssetAddress] = erc20Balances[takerAddress][ - defaultMakerAssetAddress - ].add(signedOrder.makerAssetAmount); - erc20Balances[takerAddress][defaultTakerAssetAddress] = erc20Balances[takerAddress][ - defaultTakerAssetAddress - ].minus(signedOrder.takerAssetAmount); - erc20Balances[takerAddress][zrxToken.address] = erc20Balances[takerAddress][zrxToken.address].minus( - signedOrder.takerFee, - ); - erc20Balances[feeRecipientAddress][zrxToken.address] = erc20Balances[feeRecipientAddress][ - zrxToken.address - ].add(signedOrder.makerFee.add(signedOrder.takerFee)); - }); - await exchangeWrapper.marketBuyOrdersNoThrowAsync(signedOrders, takerAddress, { - makerAssetFillAmount, - // HACK(albrow): We need to hardcode the gas estimate here because - // the Geth gas estimator doesn't work with the way we use - // delegatecall and swallow errors. - gas: 600000, - }); - - const newBalances = await erc20Wrapper.getBalancesAsync(); - expect(newBalances).to.be.deep.equal(erc20Balances); - }); - - it('should not fill a signedOrder that does not use the same makerAssetAddress', async () => { - signedOrders = [ - await orderFactory.newSignedOrderAsync(), - await orderFactory.newSignedOrderAsync(), - await orderFactory.newSignedOrderAsync({ - makerAssetData: assetDataUtils.encodeERC20AssetData(zrxToken.address), - }), - ]; - - const makerAssetFillAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(100000), 18); - const filledSignedOrders = signedOrders.slice(0, -1); - _.forEach(filledSignedOrders, signedOrder => { - erc20Balances[makerAddress][defaultMakerAssetAddress] = erc20Balances[makerAddress][ - defaultMakerAssetAddress - ].minus(signedOrder.makerAssetAmount); - erc20Balances[makerAddress][defaultTakerAssetAddress] = erc20Balances[makerAddress][ - defaultTakerAssetAddress - ].add(signedOrder.takerAssetAmount); - erc20Balances[makerAddress][zrxToken.address] = erc20Balances[makerAddress][zrxToken.address].minus( - signedOrder.makerFee, - ); - erc20Balances[takerAddress][defaultMakerAssetAddress] = erc20Balances[takerAddress][ - defaultMakerAssetAddress - ].add(signedOrder.makerAssetAmount); - erc20Balances[takerAddress][defaultTakerAssetAddress] = erc20Balances[takerAddress][ - defaultTakerAssetAddress - ].minus(signedOrder.takerAssetAmount); - erc20Balances[takerAddress][zrxToken.address] = erc20Balances[takerAddress][zrxToken.address].minus( - signedOrder.takerFee, - ); - erc20Balances[feeRecipientAddress][zrxToken.address] = erc20Balances[feeRecipientAddress][ - zrxToken.address - ].add(signedOrder.makerFee.add(signedOrder.takerFee)); - }); - await exchangeWrapper.marketBuyOrdersNoThrowAsync(signedOrders, takerAddress, { - makerAssetFillAmount, - // HACK(albrow): We need to hardcode the gas estimate here because - // the Geth gas estimator doesn't work with the way we use - // delegatecall and swallow errors. - gas: 600000, - }); - - const newBalances = await erc20Wrapper.getBalancesAsync(); - expect(newBalances).to.be.deep.equal(erc20Balances); - }); - }); - - describe('batchCancelOrders', () => { - it('should be able to cancel multiple signedOrders', async () => { - const takerAssetCancelAmounts = _.map(signedOrders, signedOrder => signedOrder.takerAssetAmount); - await exchangeWrapper.batchCancelOrdersAsync(signedOrders, makerAddress); - - await exchangeWrapper.batchFillOrdersNoThrowAsync(signedOrders, takerAddress, { - takerAssetFillAmounts: takerAssetCancelAmounts, - }); - const newBalances = await erc20Wrapper.getBalancesAsync(); - expect(erc20Balances).to.be.deep.equal(newBalances); - }); - }); - - describe('getOrdersInfo', () => { - beforeEach(async () => { - signedOrders = [ - await orderFactory.newSignedOrderAsync(), - await orderFactory.newSignedOrderAsync(), - await orderFactory.newSignedOrderAsync(), - ]; - }); - it('should get the correct information for multiple unfilled orders', async () => { - const ordersInfo = await exchangeWrapper.getOrdersInfoAsync(signedOrders); - expect(ordersInfo.length).to.be.equal(3); - _.forEach(signedOrders, (signedOrder, index) => { - const expectedOrderHash = orderHashUtils.getOrderHashHex(signedOrder); - const expectedTakerAssetFilledAmount = new BigNumber(0); - const expectedOrderStatus = OrderStatus.FILLABLE; - const orderInfo = ordersInfo[index]; - expect(orderInfo.orderHash).to.be.equal(expectedOrderHash); - expect(orderInfo.orderTakerAssetFilledAmount).to.be.bignumber.equal(expectedTakerAssetFilledAmount); - expect(orderInfo.orderStatus).to.equal(expectedOrderStatus); - }); - }); - it('should get the correct information for multiple partially filled orders', async () => { - const takerAssetFillAmounts = _.map(signedOrders, signedOrder => signedOrder.takerAssetAmount.div(2)); - await exchangeWrapper.batchFillOrdersAsync(signedOrders, takerAddress, { takerAssetFillAmounts }); - const ordersInfo = await exchangeWrapper.getOrdersInfoAsync(signedOrders); - expect(ordersInfo.length).to.be.equal(3); - _.forEach(signedOrders, (signedOrder, index) => { - const expectedOrderHash = orderHashUtils.getOrderHashHex(signedOrder); - const expectedTakerAssetFilledAmount = signedOrder.takerAssetAmount.div(2); - const expectedOrderStatus = OrderStatus.FILLABLE; - const orderInfo = ordersInfo[index]; - expect(orderInfo.orderHash).to.be.equal(expectedOrderHash); - expect(orderInfo.orderTakerAssetFilledAmount).to.be.bignumber.equal(expectedTakerAssetFilledAmount); - expect(orderInfo.orderStatus).to.equal(expectedOrderStatus); - }); - }); - it('should get the correct information for multiple fully filled orders', async () => { - await exchangeWrapper.batchFillOrdersAsync(signedOrders, takerAddress); - const ordersInfo = await exchangeWrapper.getOrdersInfoAsync(signedOrders); - expect(ordersInfo.length).to.be.equal(3); - _.forEach(signedOrders, (signedOrder, index) => { - const expectedOrderHash = orderHashUtils.getOrderHashHex(signedOrder); - const expectedTakerAssetFilledAmount = signedOrder.takerAssetAmount; - const expectedOrderStatus = OrderStatus.FULLY_FILLED; - const orderInfo = ordersInfo[index]; - expect(orderInfo.orderHash).to.be.equal(expectedOrderHash); - expect(orderInfo.orderTakerAssetFilledAmount).to.be.bignumber.equal(expectedTakerAssetFilledAmount); - expect(orderInfo.orderStatus).to.equal(expectedOrderStatus); - }); - }); - it('should get the correct information for multiple cancelled and unfilled orders', async () => { - await exchangeWrapper.batchCancelOrdersAsync(signedOrders, makerAddress); - const ordersInfo = await exchangeWrapper.getOrdersInfoAsync(signedOrders); - expect(ordersInfo.length).to.be.equal(3); - _.forEach(signedOrders, (signedOrder, index) => { - const expectedOrderHash = orderHashUtils.getOrderHashHex(signedOrder); - const expectedTakerAssetFilledAmount = new BigNumber(0); - const expectedOrderStatus = OrderStatus.CANCELLED; - const orderInfo = ordersInfo[index]; - expect(orderInfo.orderHash).to.be.equal(expectedOrderHash); - expect(orderInfo.orderTakerAssetFilledAmount).to.be.bignumber.equal(expectedTakerAssetFilledAmount); - expect(orderInfo.orderStatus).to.equal(expectedOrderStatus); - }); - }); - it('should get the correct information for multiple cancelled and partially filled orders', async () => { - const takerAssetFillAmounts = _.map(signedOrders, signedOrder => signedOrder.takerAssetAmount.div(2)); - await exchangeWrapper.batchFillOrdersAsync(signedOrders, takerAddress, { takerAssetFillAmounts }); - await exchangeWrapper.batchCancelOrdersAsync(signedOrders, makerAddress); - const ordersInfo = await exchangeWrapper.getOrdersInfoAsync(signedOrders); - expect(ordersInfo.length).to.be.equal(3); - _.forEach(signedOrders, (signedOrder, index) => { - const expectedOrderHash = orderHashUtils.getOrderHashHex(signedOrder); - const expectedTakerAssetFilledAmount = signedOrder.takerAssetAmount.div(2); - const expectedOrderStatus = OrderStatus.CANCELLED; - const orderInfo = ordersInfo[index]; - expect(orderInfo.orderHash).to.be.equal(expectedOrderHash); - expect(orderInfo.orderTakerAssetFilledAmount).to.be.bignumber.equal(expectedTakerAssetFilledAmount); - expect(orderInfo.orderStatus).to.equal(expectedOrderStatus); - }); - }); - it('should get the correct information for multiple expired and unfilled orders', async () => { - const currentTimestamp = await getLatestBlockTimestampAsync(); - const timeUntilExpiration = signedOrders[0].expirationTimeSeconds.minus(currentTimestamp).toNumber(); - await increaseTimeAndMineBlockAsync(timeUntilExpiration); - const ordersInfo = await exchangeWrapper.getOrdersInfoAsync(signedOrders); - expect(ordersInfo.length).to.be.equal(3); - _.forEach(signedOrders, (signedOrder, index) => { - const expectedOrderHash = orderHashUtils.getOrderHashHex(signedOrder); - const expectedTakerAssetFilledAmount = new BigNumber(0); - const expectedOrderStatus = OrderStatus.EXPIRED; - const orderInfo = ordersInfo[index]; - expect(orderInfo.orderHash).to.be.equal(expectedOrderHash); - expect(orderInfo.orderTakerAssetFilledAmount).to.be.bignumber.equal(expectedTakerAssetFilledAmount); - expect(orderInfo.orderStatus).to.equal(expectedOrderStatus); - }); - }); - it('should get the correct information for multiple expired and partially filled orders', async () => { - const takerAssetFillAmounts = _.map(signedOrders, signedOrder => signedOrder.takerAssetAmount.div(2)); - await exchangeWrapper.batchFillOrdersAsync(signedOrders, takerAddress, { takerAssetFillAmounts }); - const currentTimestamp = await getLatestBlockTimestampAsync(); - const timeUntilExpiration = signedOrders[0].expirationTimeSeconds.minus(currentTimestamp).toNumber(); - await increaseTimeAndMineBlockAsync(timeUntilExpiration); - const ordersInfo = await exchangeWrapper.getOrdersInfoAsync(signedOrders); - expect(ordersInfo.length).to.be.equal(3); - _.forEach(signedOrders, (signedOrder, index) => { - const expectedOrderHash = orderHashUtils.getOrderHashHex(signedOrder); - const expectedTakerAssetFilledAmount = signedOrder.takerAssetAmount.div(2); - const expectedOrderStatus = OrderStatus.EXPIRED; - const orderInfo = ordersInfo[index]; - expect(orderInfo.orderHash).to.be.equal(expectedOrderHash); - expect(orderInfo.orderTakerAssetFilledAmount).to.be.bignumber.equal(expectedTakerAssetFilledAmount); - expect(orderInfo.orderStatus).to.equal(expectedOrderStatus); - }); - }); - it('should get the correct information for a mix of unfilled, partially filled, fully filled, cancelled, and expired orders', async () => { - const unfilledOrder = await orderFactory.newSignedOrderAsync(); - const partiallyFilledOrder = await orderFactory.newSignedOrderAsync(); - await exchangeWrapper.fillOrderAsync(partiallyFilledOrder, takerAddress, { - takerAssetFillAmount: partiallyFilledOrder.takerAssetAmount.div(2), - }); - const fullyFilledOrder = await orderFactory.newSignedOrderAsync(); - await exchangeWrapper.fillOrderAsync(fullyFilledOrder, takerAddress); - const cancelledOrder = await orderFactory.newSignedOrderAsync(); - await exchangeWrapper.cancelOrderAsync(cancelledOrder, makerAddress); - const currentTimestamp = await getLatestBlockTimestampAsync(); - const expiredOrder = await orderFactory.newSignedOrderAsync({ - expirationTimeSeconds: new BigNumber(currentTimestamp), - }); - signedOrders = [unfilledOrder, partiallyFilledOrder, fullyFilledOrder, cancelledOrder, expiredOrder]; - const ordersInfo = await exchangeWrapper.getOrdersInfoAsync(signedOrders); - expect(ordersInfo.length).to.be.equal(5); - - const expectedUnfilledOrderHash = orderHashUtils.getOrderHashHex(unfilledOrder); - const expectedUnfilledTakerAssetFilledAmount = new BigNumber(0); - const expectedUnfilledOrderStatus = OrderStatus.FILLABLE; - const unfilledOrderInfo = ordersInfo[0]; - expect(unfilledOrderInfo.orderHash).to.be.equal(expectedUnfilledOrderHash); - expect(unfilledOrderInfo.orderTakerAssetFilledAmount).to.be.bignumber.equal( - expectedUnfilledTakerAssetFilledAmount, - ); - expect(unfilledOrderInfo.orderStatus).to.be.equal(expectedUnfilledOrderStatus); - - const expectedPartialOrderHash = orderHashUtils.getOrderHashHex(partiallyFilledOrder); - const expectedPartialTakerAssetFilledAmount = partiallyFilledOrder.takerAssetAmount.div(2); - const expectedPartialOrderStatus = OrderStatus.FILLABLE; - const partialOrderInfo = ordersInfo[1]; - expect(partialOrderInfo.orderHash).to.be.equal(expectedPartialOrderHash); - expect(partialOrderInfo.orderTakerAssetFilledAmount).to.be.bignumber.equal( - expectedPartialTakerAssetFilledAmount, - ); - expect(partialOrderInfo.orderStatus).to.be.equal(expectedPartialOrderStatus); - - const expectedFilledOrderHash = orderHashUtils.getOrderHashHex(fullyFilledOrder); - const expectedFilledTakerAssetFilledAmount = fullyFilledOrder.takerAssetAmount; - const expectedFilledOrderStatus = OrderStatus.FULLY_FILLED; - const filledOrderInfo = ordersInfo[2]; - expect(filledOrderInfo.orderHash).to.be.equal(expectedFilledOrderHash); - expect(filledOrderInfo.orderTakerAssetFilledAmount).to.be.bignumber.equal( - expectedFilledTakerAssetFilledAmount, - ); - expect(filledOrderInfo.orderStatus).to.be.equal(expectedFilledOrderStatus); - - const expectedCancelledOrderHash = orderHashUtils.getOrderHashHex(cancelledOrder); - const expectedCancelledTakerAssetFilledAmount = new BigNumber(0); - const expectedCancelledOrderStatus = OrderStatus.CANCELLED; - const cancelledOrderInfo = ordersInfo[3]; - expect(cancelledOrderInfo.orderHash).to.be.equal(expectedCancelledOrderHash); - expect(cancelledOrderInfo.orderTakerAssetFilledAmount).to.be.bignumber.equal( - expectedCancelledTakerAssetFilledAmount, - ); - expect(cancelledOrderInfo.orderStatus).to.be.equal(expectedCancelledOrderStatus); - - const expectedExpiredOrderHash = orderHashUtils.getOrderHashHex(expiredOrder); - const expectedExpiredTakerAssetFilledAmount = new BigNumber(0); - const expectedExpiredOrderStatus = OrderStatus.EXPIRED; - const expiredOrderInfo = ordersInfo[4]; - expect(expiredOrderInfo.orderHash).to.be.equal(expectedExpiredOrderHash); - expect(expiredOrderInfo.orderTakerAssetFilledAmount).to.be.bignumber.equal( - expectedExpiredTakerAssetFilledAmount, - ); - expect(expiredOrderInfo.orderStatus).to.be.equal(expectedExpiredOrderStatus); - }); - }); - }); -}); // tslint:disable-line:max-file-line-count diff --git a/contracts/core/test/global_hooks.ts b/contracts/core/test/global_hooks.ts deleted file mode 100644 index f8ace376a..000000000 --- a/contracts/core/test/global_hooks.ts +++ /dev/null @@ -1,17 +0,0 @@ -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/core/test/multisig/asset_proxy_owner.ts b/contracts/core/test/multisig/asset_proxy_owner.ts deleted file mode 100644 index daebfb7fb..000000000 --- a/contracts/core/test/multisig/asset_proxy_owner.ts +++ /dev/null @@ -1,506 +0,0 @@ -import { - chaiSetup, - constants, - expectContractCallFailedAsync, - expectContractCreationFailedAsync, - expectTransactionFailedAsync, - expectTransactionFailedWithoutReasonAsync, - increaseTimeAndMineBlockAsync, - provider, - sendTransactionResult, - txDefaults, - web3Wrapper, -} from '@0x/contracts-test-utils'; -import { BlockchainLifecycle } from '@0x/dev-utils'; -import { RevertReason } from '@0x/types'; -import { BigNumber } from '@0x/utils'; -import * as chai from 'chai'; -import { LogWithDecodedArgs } from 'ethereum-types'; - -import { - AssetProxyOwnerAssetProxyRegistrationEventArgs, - AssetProxyOwnerContract, - AssetProxyOwnerExecutionEventArgs, - AssetProxyOwnerExecutionFailureEventArgs, - AssetProxyOwnerSubmissionEventArgs, -} from '../../generated-wrappers/asset_proxy_owner'; -import { MixinAuthorizableContract } from '../../generated-wrappers/mixin_authorizable'; -import { TestAssetProxyOwnerContract } from '../../generated-wrappers/test_asset_proxy_owner'; -import { artifacts } from '../../src/artifacts'; -import { AssetProxyOwnerWrapper } from '../utils/asset_proxy_owner_wrapper'; - -chaiSetup.configure(); -const expect = chai.expect; -const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); -// tslint:disable:no-unnecessary-type-assertion -describe('AssetProxyOwner', () => { - let owners: string[]; - let authorized: string; - let notOwner: string; - const REQUIRED_APPROVALS = new BigNumber(2); - const SECONDS_TIME_LOCKED = new BigNumber(1000000); - - let erc20Proxy: MixinAuthorizableContract; - let erc721Proxy: MixinAuthorizableContract; - let testAssetProxyOwner: TestAssetProxyOwnerContract; - let assetProxyOwnerWrapper: AssetProxyOwnerWrapper; - - before(async () => { - await blockchainLifecycle.startAsync(); - }); - after(async () => { - await blockchainLifecycle.revertAsync(); - }); - before(async () => { - const accounts = await web3Wrapper.getAvailableAddressesAsync(); - owners = [accounts[0], accounts[1]]; - authorized = accounts[2]; - notOwner = accounts[3]; - const initialOwner = accounts[0]; - erc20Proxy = await MixinAuthorizableContract.deployFrom0xArtifactAsync( - artifacts.MixinAuthorizable, - provider, - txDefaults, - ); - erc721Proxy = await MixinAuthorizableContract.deployFrom0xArtifactAsync( - artifacts.MixinAuthorizable, - provider, - txDefaults, - ); - const defaultAssetProxyContractAddresses: string[] = []; - testAssetProxyOwner = await TestAssetProxyOwnerContract.deployFrom0xArtifactAsync( - artifacts.TestAssetProxyOwner, - provider, - txDefaults, - owners, - defaultAssetProxyContractAddresses, - REQUIRED_APPROVALS, - SECONDS_TIME_LOCKED, - ); - assetProxyOwnerWrapper = new AssetProxyOwnerWrapper(testAssetProxyOwner, provider); - await web3Wrapper.awaitTransactionSuccessAsync( - await erc20Proxy.transferOwnership.sendTransactionAsync(testAssetProxyOwner.address, { - from: initialOwner, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - await web3Wrapper.awaitTransactionSuccessAsync( - await erc721Proxy.transferOwnership.sendTransactionAsync(testAssetProxyOwner.address, { - from: initialOwner, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - }); - beforeEach(async () => { - await blockchainLifecycle.startAsync(); - }); - afterEach(async () => { - await blockchainLifecycle.revertAsync(); - }); - - describe('constructor', () => { - it('should register passed in assetProxyContracts', async () => { - const assetProxyContractAddresses = [erc20Proxy.address, erc721Proxy.address]; - const newMultiSig = await AssetProxyOwnerContract.deployFrom0xArtifactAsync( - artifacts.AssetProxyOwner, - provider, - txDefaults, - owners, - assetProxyContractAddresses, - REQUIRED_APPROVALS, - SECONDS_TIME_LOCKED, - ); - const isErc20ProxyRegistered = await newMultiSig.isAssetProxyRegistered.callAsync(erc20Proxy.address); - const isErc721ProxyRegistered = await newMultiSig.isAssetProxyRegistered.callAsync(erc721Proxy.address); - expect(isErc20ProxyRegistered).to.equal(true); - expect(isErc721ProxyRegistered).to.equal(true); - }); - it('should throw if a null address is included in assetProxyContracts', async () => { - const assetProxyContractAddresses = [erc20Proxy.address, constants.NULL_ADDRESS]; - return expectContractCreationFailedAsync( - (AssetProxyOwnerContract.deployFrom0xArtifactAsync( - artifacts.AssetProxyOwner, - provider, - txDefaults, - owners, - assetProxyContractAddresses, - REQUIRED_APPROVALS, - SECONDS_TIME_LOCKED, - ) as any) as sendTransactionResult, - RevertReason.InvalidAssetProxy, - ); - }); - }); - - describe('isFunctionRemoveAuthorizedAddressAtIndex', () => { - it('should return false if data is not for removeAuthorizedAddressAtIndex', async () => { - const notRemoveAuthorizedAddressData = erc20Proxy.addAuthorizedAddress.getABIEncodedTransactionData( - owners[0], - ); - - const isFunctionRemoveAuthorizedAddressAtIndex = await testAssetProxyOwner.isFunctionRemoveAuthorizedAddressAtIndex.callAsync( - notRemoveAuthorizedAddressData, - ); - expect(isFunctionRemoveAuthorizedAddressAtIndex).to.be.false(); - }); - - it('should return true if data is for removeAuthorizedAddressAtIndex', async () => { - const index = new BigNumber(0); - const removeAuthorizedAddressAtIndexData = erc20Proxy.removeAuthorizedAddressAtIndex.getABIEncodedTransactionData( - owners[0], - index, - ); - const isFunctionRemoveAuthorizedAddressAtIndex = await testAssetProxyOwner.isFunctionRemoveAuthorizedAddressAtIndex.callAsync( - removeAuthorizedAddressAtIndexData, - ); - expect(isFunctionRemoveAuthorizedAddressAtIndex).to.be.true(); - }); - }); - - describe('registerAssetProxy', () => { - it('should throw if not called by multisig', async () => { - const isRegistered = true; - return expectTransactionFailedWithoutReasonAsync( - testAssetProxyOwner.registerAssetProxy.sendTransactionAsync(erc20Proxy.address, isRegistered, { - from: owners[0], - }), - ); - }); - - it('should register an address if called by multisig after timelock', async () => { - const addressToRegister = erc20Proxy.address; - const isRegistered = true; - const registerAssetProxyData = testAssetProxyOwner.registerAssetProxy.getABIEncodedTransactionData( - addressToRegister, - isRegistered, - ); - const submitTxRes = await assetProxyOwnerWrapper.submitTransactionAsync( - testAssetProxyOwner.address, - registerAssetProxyData, - owners[0], - ); - - const log = submitTxRes.logs[0] as LogWithDecodedArgs<AssetProxyOwnerSubmissionEventArgs>; - const txId = log.args.transactionId; - - await assetProxyOwnerWrapper.confirmTransactionAsync(txId, owners[1]); - await increaseTimeAndMineBlockAsync(SECONDS_TIME_LOCKED.toNumber()); - - const executeTxRes = await assetProxyOwnerWrapper.executeTransactionAsync(txId, owners[0]); - const registerLog = executeTxRes.logs[0] as LogWithDecodedArgs< - AssetProxyOwnerAssetProxyRegistrationEventArgs - >; - expect(registerLog.args.assetProxyContract).to.equal(addressToRegister); - expect(registerLog.args.isRegistered).to.equal(isRegistered); - - const isAssetProxyRegistered = await testAssetProxyOwner.isAssetProxyRegistered.callAsync( - addressToRegister, - ); - expect(isAssetProxyRegistered).to.equal(isRegistered); - }); - - it('should fail if registering a null address', async () => { - const addressToRegister = constants.NULL_ADDRESS; - const isRegistered = true; - const registerAssetProxyData = testAssetProxyOwner.registerAssetProxy.getABIEncodedTransactionData( - addressToRegister, - isRegistered, - ); - const submitTxRes = await assetProxyOwnerWrapper.submitTransactionAsync( - testAssetProxyOwner.address, - registerAssetProxyData, - owners[0], - ); - const log = submitTxRes.logs[0] as LogWithDecodedArgs<AssetProxyOwnerSubmissionEventArgs>; - const txId = log.args.transactionId; - - await assetProxyOwnerWrapper.confirmTransactionAsync(txId, owners[1]); - await increaseTimeAndMineBlockAsync(SECONDS_TIME_LOCKED.toNumber()); - - const executeTxRes = await assetProxyOwnerWrapper.executeTransactionAsync(txId, owners[0]); - const failureLog = executeTxRes.logs[0] as LogWithDecodedArgs<AssetProxyOwnerExecutionFailureEventArgs>; - expect(failureLog.args.transactionId).to.be.bignumber.equal(txId); - - const isAssetProxyRegistered = await testAssetProxyOwner.isAssetProxyRegistered.callAsync( - addressToRegister, - ); - expect(isAssetProxyRegistered).to.equal(false); - }); - }); - - describe('Calling removeAuthorizedAddressAtIndex', () => { - const erc20Index = new BigNumber(0); - const erc721Index = new BigNumber(1); - before('authorize both proxies and register erc20 proxy', async () => { - // Only register ERC20 proxy - const addressToRegister = erc20Proxy.address; - const isRegistered = true; - const registerAssetProxyData = testAssetProxyOwner.registerAssetProxy.getABIEncodedTransactionData( - addressToRegister, - isRegistered, - ); - const registerAssetProxySubmitRes = await assetProxyOwnerWrapper.submitTransactionAsync( - testAssetProxyOwner.address, - registerAssetProxyData, - owners[0], - ); - const registerAssetProxySubmitLog = registerAssetProxySubmitRes.logs[0] as LogWithDecodedArgs< - AssetProxyOwnerSubmissionEventArgs - >; - - const addAuthorizedAddressData = erc20Proxy.addAuthorizedAddress.getABIEncodedTransactionData(authorized); - const erc20AddAuthorizedAddressSubmitRes = await assetProxyOwnerWrapper.submitTransactionAsync( - erc20Proxy.address, - addAuthorizedAddressData, - owners[0], - ); - const erc721AddAuthorizedAddressSubmitRes = await assetProxyOwnerWrapper.submitTransactionAsync( - erc721Proxy.address, - addAuthorizedAddressData, - owners[0], - ); - const erc20AddAuthorizedAddressSubmitLog = erc20AddAuthorizedAddressSubmitRes.logs[0] as LogWithDecodedArgs< - AssetProxyOwnerSubmissionEventArgs - >; - const erc721AddAuthorizedAddressSubmitLog = erc721AddAuthorizedAddressSubmitRes - .logs[0] as LogWithDecodedArgs<AssetProxyOwnerSubmissionEventArgs>; - - const registerAssetProxyTxId = registerAssetProxySubmitLog.args.transactionId; - const erc20AddAuthorizedAddressTxId = erc20AddAuthorizedAddressSubmitLog.args.transactionId; - const erc721AddAuthorizedAddressTxId = erc721AddAuthorizedAddressSubmitLog.args.transactionId; - - await assetProxyOwnerWrapper.confirmTransactionAsync(registerAssetProxyTxId, owners[1]); - await assetProxyOwnerWrapper.confirmTransactionAsync(erc20AddAuthorizedAddressTxId, owners[1]); - await assetProxyOwnerWrapper.confirmTransactionAsync(erc721AddAuthorizedAddressTxId, owners[1]); - await increaseTimeAndMineBlockAsync(SECONDS_TIME_LOCKED.toNumber()); - await assetProxyOwnerWrapper.executeTransactionAsync(registerAssetProxyTxId, owners[0]); - await assetProxyOwnerWrapper.executeTransactionAsync(erc20AddAuthorizedAddressTxId, owners[0], { - gas: constants.MAX_EXECUTE_TRANSACTION_GAS, - }); - await assetProxyOwnerWrapper.executeTransactionAsync(erc721AddAuthorizedAddressTxId, owners[0], { - gas: constants.MAX_EXECUTE_TRANSACTION_GAS, - }); - }); - - describe('validRemoveAuthorizedAddressAtIndexTx', () => { - it('should revert if data is not for removeAuthorizedAddressAtIndex and proxy is registered', async () => { - const notRemoveAuthorizedAddressData = erc20Proxy.addAuthorizedAddress.getABIEncodedTransactionData( - authorized, - ); - const submitTxRes = await assetProxyOwnerWrapper.submitTransactionAsync( - erc20Proxy.address, - notRemoveAuthorizedAddressData, - owners[0], - ); - const log = submitTxRes.logs[0] as LogWithDecodedArgs<AssetProxyOwnerSubmissionEventArgs>; - const txId = log.args.transactionId; - return expectContractCallFailedAsync( - testAssetProxyOwner.testValidRemoveAuthorizedAddressAtIndexTx.callAsync(txId), - RevertReason.InvalidFunctionSelector, - ); - }); - - it('should return true if data is for removeAuthorizedAddressAtIndex and proxy is registered', async () => { - const removeAuthorizedAddressAtIndexData = erc20Proxy.removeAuthorizedAddressAtIndex.getABIEncodedTransactionData( - authorized, - erc20Index, - ); - const submitTxRes = await assetProxyOwnerWrapper.submitTransactionAsync( - erc20Proxy.address, - removeAuthorizedAddressAtIndexData, - owners[0], - ); - const log = submitTxRes.logs[0] as LogWithDecodedArgs<AssetProxyOwnerSubmissionEventArgs>; - const txId = log.args.transactionId; - const isValidRemoveAuthorizedAddressAtIndexTx = await testAssetProxyOwner.testValidRemoveAuthorizedAddressAtIndexTx.callAsync( - txId, - ); - expect(isValidRemoveAuthorizedAddressAtIndexTx).to.be.true(); - }); - - it('should revert if data is for removeAuthorizedAddressAtIndex and proxy is not registered', async () => { - const removeAuthorizedAddressAtIndexData = erc721Proxy.removeAuthorizedAddressAtIndex.getABIEncodedTransactionData( - authorized, - erc721Index, - ); - const submitTxRes = await assetProxyOwnerWrapper.submitTransactionAsync( - erc721Proxy.address, - removeAuthorizedAddressAtIndexData, - owners[0], - ); - const log = submitTxRes.logs[0] as LogWithDecodedArgs<AssetProxyOwnerSubmissionEventArgs>; - const txId = log.args.transactionId; - return expectContractCallFailedAsync( - testAssetProxyOwner.testValidRemoveAuthorizedAddressAtIndexTx.callAsync(txId), - RevertReason.UnregisteredAssetProxy, - ); - }); - }); - - describe('executeRemoveAuthorizedAddressAtIndex', () => { - it('should throw without the required confirmations', async () => { - const removeAuthorizedAddressAtIndexData = erc20Proxy.removeAuthorizedAddressAtIndex.getABIEncodedTransactionData( - authorized, - erc20Index, - ); - const res = await assetProxyOwnerWrapper.submitTransactionAsync( - erc20Proxy.address, - removeAuthorizedAddressAtIndexData, - owners[0], - ); - const log = res.logs[0] as LogWithDecodedArgs<AssetProxyOwnerSubmissionEventArgs>; - const txId = log.args.transactionId; - - return expectTransactionFailedAsync( - testAssetProxyOwner.executeRemoveAuthorizedAddressAtIndex.sendTransactionAsync(txId, { - from: owners[1], - }), - RevertReason.TxNotFullyConfirmed, - ); - }); - - it('should throw if tx destination is not registered', async () => { - const removeAuthorizedAddressAtIndexData = erc721Proxy.removeAuthorizedAddressAtIndex.getABIEncodedTransactionData( - authorized, - erc721Index, - ); - const res = await assetProxyOwnerWrapper.submitTransactionAsync( - erc721Proxy.address, - removeAuthorizedAddressAtIndexData, - owners[0], - ); - const log = res.logs[0] as LogWithDecodedArgs<AssetProxyOwnerSubmissionEventArgs>; - const txId = log.args.transactionId; - - await assetProxyOwnerWrapper.confirmTransactionAsync(txId, owners[1]); - - return expectTransactionFailedAsync( - testAssetProxyOwner.executeRemoveAuthorizedAddressAtIndex.sendTransactionAsync(txId, { - from: owners[1], - }), - RevertReason.UnregisteredAssetProxy, - ); - }); - - it('should throw if tx data is not for removeAuthorizedAddressAtIndex', async () => { - const newAuthorized = owners[1]; - const addAuthorizedAddressData = erc20Proxy.addAuthorizedAddress.getABIEncodedTransactionData( - newAuthorized, - ); - const res = await assetProxyOwnerWrapper.submitTransactionAsync( - erc20Proxy.address, - addAuthorizedAddressData, - owners[0], - ); - const log = res.logs[0] as LogWithDecodedArgs<AssetProxyOwnerSubmissionEventArgs>; - const txId = log.args.transactionId; - - await assetProxyOwnerWrapper.confirmTransactionAsync(txId, owners[1]); - - return expectTransactionFailedAsync( - testAssetProxyOwner.executeRemoveAuthorizedAddressAtIndex.sendTransactionAsync(txId, { - from: owners[1], - }), - RevertReason.InvalidFunctionSelector, - ); - }); - - it('should execute removeAuthorizedAddressAtIndex for registered address if fully confirmed and called by owner', async () => { - const isAuthorizedBefore = await erc20Proxy.authorized.callAsync(authorized); - expect(isAuthorizedBefore).to.equal(true); - - const removeAuthorizedAddressAtIndexData = erc20Proxy.removeAuthorizedAddressAtIndex.getABIEncodedTransactionData( - authorized, - erc20Index, - ); - const submitRes = await assetProxyOwnerWrapper.submitTransactionAsync( - erc20Proxy.address, - removeAuthorizedAddressAtIndexData, - owners[0], - ); - const submitLog = submitRes.logs[0] as LogWithDecodedArgs<AssetProxyOwnerSubmissionEventArgs>; - const txId = submitLog.args.transactionId; - - await assetProxyOwnerWrapper.confirmTransactionAsync(txId, owners[1]); - - const execRes = await assetProxyOwnerWrapper.executeRemoveAuthorizedAddressAtIndexAsync( - txId, - owners[0], - ); - const execLog = execRes.logs[1] as LogWithDecodedArgs<AssetProxyOwnerExecutionEventArgs>; - expect(execLog.args.transactionId).to.be.bignumber.equal(txId); - - const tx = await testAssetProxyOwner.transactions.callAsync(txId); - const isExecuted = tx[3]; - expect(isExecuted).to.equal(true); - - const isAuthorizedAfter = await erc20Proxy.authorized.callAsync(authorized); - expect(isAuthorizedAfter).to.equal(false); - }); - - it('should execute removeAuthorizedAddressAtIndex for registered address if fully confirmed and called by non-owner', async () => { - const isAuthorizedBefore = await erc20Proxy.authorized.callAsync(authorized); - expect(isAuthorizedBefore).to.equal(true); - - const removeAuthorizedAddressAtIndexData = erc20Proxy.removeAuthorizedAddressAtIndex.getABIEncodedTransactionData( - authorized, - erc20Index, - ); - const submitRes = await assetProxyOwnerWrapper.submitTransactionAsync( - erc20Proxy.address, - removeAuthorizedAddressAtIndexData, - owners[0], - ); - const submitLog = submitRes.logs[0] as LogWithDecodedArgs<AssetProxyOwnerSubmissionEventArgs>; - const txId = submitLog.args.transactionId; - - await assetProxyOwnerWrapper.confirmTransactionAsync(txId, owners[1]); - - const execRes = await assetProxyOwnerWrapper.executeRemoveAuthorizedAddressAtIndexAsync(txId, notOwner); - const execLog = execRes.logs[1] as LogWithDecodedArgs<AssetProxyOwnerExecutionEventArgs>; - expect(execLog.args.transactionId).to.be.bignumber.equal(txId); - - const tx = await testAssetProxyOwner.transactions.callAsync(txId); - const isExecuted = tx[3]; - expect(isExecuted).to.equal(true); - - const isAuthorizedAfter = await erc20Proxy.authorized.callAsync(authorized); - expect(isAuthorizedAfter).to.equal(false); - }); - - it('should throw if already executed', async () => { - const removeAuthorizedAddressAtIndexData = erc20Proxy.removeAuthorizedAddressAtIndex.getABIEncodedTransactionData( - authorized, - erc20Index, - ); - const submitRes = await assetProxyOwnerWrapper.submitTransactionAsync( - erc20Proxy.address, - removeAuthorizedAddressAtIndexData, - owners[0], - ); - const submitLog = submitRes.logs[0] as LogWithDecodedArgs<AssetProxyOwnerSubmissionEventArgs>; - const txId = submitLog.args.transactionId; - - await assetProxyOwnerWrapper.confirmTransactionAsync(txId, owners[1]); - - const execRes = await assetProxyOwnerWrapper.executeRemoveAuthorizedAddressAtIndexAsync( - txId, - owners[0], - ); - const execLog = execRes.logs[1] as LogWithDecodedArgs<AssetProxyOwnerExecutionEventArgs>; - expect(execLog.args.transactionId).to.be.bignumber.equal(txId); - - const tx = await testAssetProxyOwner.transactions.callAsync(txId); - const isExecuted = tx[3]; - expect(isExecuted).to.equal(true); - - return expectTransactionFailedWithoutReasonAsync( - testAssetProxyOwner.executeRemoveAuthorizedAddressAtIndex.sendTransactionAsync(txId, { - from: owners[1], - }), - ); - }); - }); - }); -}); -// tslint:disable-line max-file-line-count diff --git a/contracts/core/test/utils/asset_proxy_owner_wrapper.ts b/contracts/core/test/utils/asset_proxy_owner_wrapper.ts deleted file mode 100644 index df23658d8..000000000 --- a/contracts/core/test/utils/asset_proxy_owner_wrapper.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { LogDecoder } from '@0x/contracts-test-utils'; -import { artifacts as tokensArtifacts } from '@0x/contracts-tokens'; -import { BigNumber } from '@0x/utils'; -import { Web3Wrapper } from '@0x/web3-wrapper'; -import { Provider, TransactionReceiptWithDecodedLogs } from 'ethereum-types'; -import * as _ from 'lodash'; - -import { AssetProxyOwnerContract } from '../../generated-wrappers/asset_proxy_owner'; -import { artifacts } from '../../src/artifacts'; - -export class AssetProxyOwnerWrapper { - private readonly _assetProxyOwner: AssetProxyOwnerContract; - private readonly _web3Wrapper: Web3Wrapper; - private readonly _logDecoder: LogDecoder; - constructor(assetproxyOwnerContract: AssetProxyOwnerContract, provider: Provider) { - this._assetProxyOwner = assetproxyOwnerContract; - this._web3Wrapper = new Web3Wrapper(provider); - this._logDecoder = new LogDecoder(this._web3Wrapper, { ...artifacts, ...tokensArtifacts }); - } - public async submitTransactionAsync( - destination: string, - data: string, - from: string, - opts: { value?: BigNumber } = {}, - ): Promise<TransactionReceiptWithDecodedLogs> { - const value = _.isUndefined(opts.value) ? new BigNumber(0) : opts.value; - const txHash = await this._assetProxyOwner.submitTransaction.sendTransactionAsync(destination, value, data, { - from, - }); - const tx = await this._logDecoder.getTxWithDecodedLogsAsync(txHash); - return tx; - } - public async confirmTransactionAsync(txId: BigNumber, from: string): Promise<TransactionReceiptWithDecodedLogs> { - const txHash = await this._assetProxyOwner.confirmTransaction.sendTransactionAsync(txId, { from }); - const tx = await this._logDecoder.getTxWithDecodedLogsAsync(txHash); - return tx; - } - public async revokeConfirmationAsync(txId: BigNumber, from: string): Promise<TransactionReceiptWithDecodedLogs> { - const txHash = await this._assetProxyOwner.revokeConfirmation.sendTransactionAsync(txId, { from }); - const tx = await this._logDecoder.getTxWithDecodedLogsAsync(txHash); - return tx; - } - public async executeTransactionAsync( - txId: BigNumber, - from: string, - opts: { gas?: number } = {}, - ): Promise<TransactionReceiptWithDecodedLogs> { - const txHash = await this._assetProxyOwner.executeTransaction.sendTransactionAsync(txId, { - from, - gas: opts.gas, - }); - const tx = await this._logDecoder.getTxWithDecodedLogsAsync(txHash); - return tx; - } - public async executeRemoveAuthorizedAddressAtIndexAsync( - txId: BigNumber, - from: string, - ): Promise<TransactionReceiptWithDecodedLogs> { - // tslint:disable-next-line:no-unnecessary-type-assertion - const txHash = await (this - ._assetProxyOwner as AssetProxyOwnerContract).executeRemoveAuthorizedAddressAtIndex.sendTransactionAsync( - txId, - { - from, - }, - ); - const tx = await this._logDecoder.getTxWithDecodedLogsAsync(txHash); - return tx; - } -} diff --git a/contracts/core/test/utils/asset_wrapper.ts b/contracts/core/test/utils/asset_wrapper.ts deleted file mode 100644 index e4090ad74..000000000 --- a/contracts/core/test/utils/asset_wrapper.ts +++ /dev/null @@ -1,222 +0,0 @@ -import { AbstractAssetWrapper, constants } from '@0x/contracts-test-utils'; -import { assetDataUtils } from '@0x/order-utils'; -import { AssetProxyId } from '@0x/types'; -import { BigNumber, errorUtils } from '@0x/utils'; -import * as _ from 'lodash'; - -import { ERC20Wrapper } from './erc20_wrapper'; -import { ERC721Wrapper } from './erc721_wrapper'; - -interface ProxyIdToAssetWrappers { - [proxyId: string]: AbstractAssetWrapper; -} - -/** - * This class abstracts away the differences between ERC20 and ERC721 tokens so that - * the logic that uses it does not need to care what standard a token belongs to. - */ -export class AssetWrapper { - private readonly _proxyIdToAssetWrappers: ProxyIdToAssetWrappers; - constructor(assetWrappers: AbstractAssetWrapper[]) { - this._proxyIdToAssetWrappers = {}; - _.each(assetWrappers, assetWrapper => { - const proxyId = assetWrapper.getProxyId(); - this._proxyIdToAssetWrappers[proxyId] = assetWrapper; - }); - } - public async getBalanceAsync(userAddress: string, assetData: string): Promise<BigNumber> { - const proxyId = assetDataUtils.decodeAssetProxyId(assetData); - switch (proxyId) { - case AssetProxyId.ERC20: { - const erc20Wrapper = this._proxyIdToAssetWrappers[proxyId] as ERC20Wrapper; - const balance = await erc20Wrapper.getBalanceAsync(userAddress, assetData); - return balance; - } - case AssetProxyId.ERC721: { - const assetWrapper = this._proxyIdToAssetWrappers[proxyId] as ERC721Wrapper; - const assetProxyData = assetDataUtils.decodeERC721AssetData(assetData); - const isOwner = await assetWrapper.isOwnerAsync( - userAddress, - assetProxyData.tokenAddress, - assetProxyData.tokenId, - ); - const balance = isOwner ? new BigNumber(1) : new BigNumber(0); - return balance; - } - default: - throw errorUtils.spawnSwitchErr('proxyId', proxyId); - } - } - public async setBalanceAsync(userAddress: string, assetData: string, desiredBalance: BigNumber): Promise<void> { - const proxyId = assetDataUtils.decodeAssetProxyId(assetData); - switch (proxyId) { - case AssetProxyId.ERC20: { - const erc20Wrapper = this._proxyIdToAssetWrappers[proxyId] as ERC20Wrapper; - await erc20Wrapper.setBalanceAsync(userAddress, assetData, desiredBalance); - return; - } - case AssetProxyId.ERC721: { - if (!desiredBalance.eq(0) && !desiredBalance.eq(1)) { - throw new Error(`Balance for ERC721 token can only be set to 0 or 1. Got: ${desiredBalance}`); - } - const erc721Wrapper = this._proxyIdToAssetWrappers[proxyId] as ERC721Wrapper; - const assetProxyData = assetDataUtils.decodeERC721AssetData(assetData); - const doesTokenExist = erc721Wrapper.doesTokenExistAsync( - assetProxyData.tokenAddress, - assetProxyData.tokenId, - ); - if (!doesTokenExist && desiredBalance.eq(1)) { - await erc721Wrapper.mintAsync(assetProxyData.tokenAddress, assetProxyData.tokenId, userAddress); - return; - } else if (!doesTokenExist && desiredBalance.eq(0)) { - return; // noop - } - const tokenOwner = await erc721Wrapper.ownerOfAsync( - assetProxyData.tokenAddress, - assetProxyData.tokenId, - ); - if (userAddress !== tokenOwner && desiredBalance.eq(1)) { - await erc721Wrapper.transferFromAsync( - assetProxyData.tokenAddress, - assetProxyData.tokenId, - tokenOwner, - userAddress, - ); - } else if (tokenOwner === userAddress && desiredBalance.eq(0)) { - // Transfer token to someone else - const userAddresses = await (erc721Wrapper as any)._web3Wrapper.getAvailableAddressesAsync(); - const nonOwner = _.find(userAddresses, a => a !== userAddress); - await erc721Wrapper.transferFromAsync( - assetProxyData.tokenAddress, - assetProxyData.tokenId, - tokenOwner, - nonOwner, - ); - return; - } else if ( - (userAddress !== tokenOwner && desiredBalance.eq(0)) || - (tokenOwner === userAddress && desiredBalance.eq(1)) - ) { - return; // noop - } - break; - } - default: - throw errorUtils.spawnSwitchErr('proxyId', proxyId); - } - } - public async getProxyAllowanceAsync(userAddress: string, assetData: string): Promise<BigNumber> { - const proxyId = assetDataUtils.decodeAssetProxyId(assetData); - switch (proxyId) { - case AssetProxyId.ERC20: { - const erc20Wrapper = this._proxyIdToAssetWrappers[proxyId] as ERC20Wrapper; - const allowance = await erc20Wrapper.getProxyAllowanceAsync(userAddress, assetData); - return allowance; - } - case AssetProxyId.ERC721: { - const assetWrapper = this._proxyIdToAssetWrappers[proxyId] as ERC721Wrapper; - const erc721ProxyData = assetDataUtils.decodeERC721AssetData(assetData); - const isProxyApprovedForAll = await assetWrapper.isProxyApprovedForAllAsync( - userAddress, - erc721ProxyData.tokenAddress, - ); - if (isProxyApprovedForAll) { - return constants.UNLIMITED_ALLOWANCE_IN_BASE_UNITS; - } - - const isProxyApproved = await assetWrapper.isProxyApprovedAsync( - erc721ProxyData.tokenAddress, - erc721ProxyData.tokenId, - ); - const allowance = isProxyApproved ? new BigNumber(1) : new BigNumber(0); - return allowance; - } - default: - throw errorUtils.spawnSwitchErr('proxyId', proxyId); - } - } - public async setProxyAllowanceAsync( - userAddress: string, - assetData: string, - desiredAllowance: BigNumber, - ): Promise<void> { - const proxyId = assetDataUtils.decodeAssetProxyId(assetData); - switch (proxyId) { - case AssetProxyId.ERC20: { - const erc20Wrapper = this._proxyIdToAssetWrappers[proxyId] as ERC20Wrapper; - await erc20Wrapper.setAllowanceAsync(userAddress, assetData, desiredAllowance); - return; - } - case AssetProxyId.ERC721: { - if ( - !desiredAllowance.eq(0) && - !desiredAllowance.eq(1) && - !desiredAllowance.eq(constants.UNLIMITED_ALLOWANCE_IN_BASE_UNITS) - ) { - throw new Error( - `Allowance for ERC721 token can only be set to 0, 1 or 2^256-1. Got: ${desiredAllowance}`, - ); - } - const erc721Wrapper = this._proxyIdToAssetWrappers[proxyId] as ERC721Wrapper; - const assetProxyData = assetDataUtils.decodeERC721AssetData(assetData); - - const doesTokenExist = await erc721Wrapper.doesTokenExistAsync( - assetProxyData.tokenAddress, - assetProxyData.tokenId, - ); - if (!doesTokenExist) { - throw new Error( - `Cannot setProxyAllowance on non-existent token: ${assetProxyData.tokenAddress} ${ - assetProxyData.tokenId - }`, - ); - } - const isProxyApprovedForAll = await erc721Wrapper.isProxyApprovedForAllAsync( - userAddress, - assetProxyData.tokenAddress, - ); - if (!isProxyApprovedForAll && desiredAllowance.eq(constants.UNLIMITED_ALLOWANCE_IN_BASE_UNITS)) { - const isApproved = true; - await erc721Wrapper.approveProxyForAllAsync( - assetProxyData.tokenAddress, - assetProxyData.tokenId, - isApproved, - ); - } else if (isProxyApprovedForAll && desiredAllowance.eq(0)) { - const isApproved = false; - await erc721Wrapper.approveProxyForAllAsync( - assetProxyData.tokenAddress, - assetProxyData.tokenId, - isApproved, - ); - } else if (isProxyApprovedForAll && desiredAllowance.eq(constants.UNLIMITED_ALLOWANCE_IN_BASE_UNITS)) { - return; // Noop - } - - const isProxyApproved = await erc721Wrapper.isProxyApprovedAsync( - assetProxyData.tokenAddress, - assetProxyData.tokenId, - ); - if (!isProxyApproved && desiredAllowance.eq(1)) { - await erc721Wrapper.approveProxyAsync(assetProxyData.tokenAddress, assetProxyData.tokenId); - } else if (isProxyApproved && desiredAllowance.eq(0)) { - // Remove approval - await erc721Wrapper.approveAsync( - constants.NULL_ADDRESS, - assetProxyData.tokenAddress, - assetProxyData.tokenId, - ); - } else if ( - (!isProxyApproved && desiredAllowance.eq(0)) || - (isProxyApproved && desiredAllowance.eq(1)) - ) { - return; // noop - } - - break; - } - default: - throw errorUtils.spawnSwitchErr('proxyId', proxyId); - } - } -} diff --git a/contracts/core/test/utils/erc20_wrapper.ts b/contracts/core/test/utils/erc20_wrapper.ts deleted file mode 100644 index e5ab0e36c..000000000 --- a/contracts/core/test/utils/erc20_wrapper.ts +++ /dev/null @@ -1,179 +0,0 @@ -import { constants, ERC20BalancesByOwner, txDefaults } from '@0x/contracts-test-utils'; -import { artifacts as tokensArtifacts, DummyERC20TokenContract } from '@0x/contracts-tokens'; -import { assetDataUtils } from '@0x/order-utils'; -import { BigNumber } from '@0x/utils'; -import { Web3Wrapper } from '@0x/web3-wrapper'; -import { Provider } from 'ethereum-types'; -import * as _ from 'lodash'; - -import { ERC20ProxyContract } from '../../generated-wrappers/erc20_proxy'; -import { artifacts } from '../../src/artifacts'; - -export class ERC20Wrapper { - private readonly _tokenOwnerAddresses: string[]; - private readonly _contractOwnerAddress: string; - private readonly _web3Wrapper: Web3Wrapper; - private readonly _provider: Provider; - private readonly _dummyTokenContracts: DummyERC20TokenContract[]; - private _proxyContract?: ERC20ProxyContract; - private _proxyIdIfExists?: string; - /** - * Instanitates an ERC20Wrapper - * @param provider Web3 provider to use for all JSON RPC requests - * @param tokenOwnerAddresses Addresses that we want to endow as owners for dummy ERC20 tokens - * @param contractOwnerAddress Desired owner of the contract - * Instance of ERC20Wrapper - */ - constructor(provider: Provider, tokenOwnerAddresses: string[], contractOwnerAddress: string) { - this._dummyTokenContracts = []; - this._web3Wrapper = new Web3Wrapper(provider); - this._provider = provider; - this._tokenOwnerAddresses = tokenOwnerAddresses; - this._contractOwnerAddress = contractOwnerAddress; - } - public async deployDummyTokensAsync( - numberToDeploy: number, - decimals: BigNumber, - ): Promise<DummyERC20TokenContract[]> { - for (let i = 0; i < numberToDeploy; i++) { - this._dummyTokenContracts.push( - await DummyERC20TokenContract.deployFrom0xArtifactAsync( - tokensArtifacts.DummyERC20Token, - this._provider, - txDefaults, - constants.DUMMY_TOKEN_NAME, - constants.DUMMY_TOKEN_SYMBOL, - decimals, - constants.DUMMY_TOKEN_TOTAL_SUPPLY, - ), - ); - } - return this._dummyTokenContracts; - } - public async deployProxyAsync(): Promise<ERC20ProxyContract> { - this._proxyContract = await ERC20ProxyContract.deployFrom0xArtifactAsync( - artifacts.ERC20Proxy, - this._provider, - txDefaults, - ); - this._proxyIdIfExists = await this._proxyContract.getProxyId.callAsync(); - return this._proxyContract; - } - public getProxyId(): string { - this._validateProxyContractExistsOrThrow(); - return this._proxyIdIfExists as string; - } - public async setBalancesAndAllowancesAsync(): Promise<void> { - this._validateDummyTokenContractsExistOrThrow(); - this._validateProxyContractExistsOrThrow(); - for (const dummyTokenContract of this._dummyTokenContracts) { - for (const tokenOwnerAddress of this._tokenOwnerAddresses) { - await this._web3Wrapper.awaitTransactionSuccessAsync( - await dummyTokenContract.setBalance.sendTransactionAsync( - tokenOwnerAddress, - constants.INITIAL_ERC20_BALANCE, - { from: this._contractOwnerAddress }, - ), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - await this._web3Wrapper.awaitTransactionSuccessAsync( - await dummyTokenContract.approve.sendTransactionAsync( - (this._proxyContract as ERC20ProxyContract).address, - constants.INITIAL_ERC20_ALLOWANCE, - { from: tokenOwnerAddress }, - ), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - } - } - } - public async getBalanceAsync(userAddress: string, assetData: string): Promise<BigNumber> { - const tokenContract = this._getTokenContractFromAssetData(assetData); - const balance = new BigNumber(await tokenContract.balanceOf.callAsync(userAddress)); - return balance; - } - public async setBalanceAsync(userAddress: string, assetData: string, amount: BigNumber): Promise<void> { - const tokenContract = this._getTokenContractFromAssetData(assetData); - await this._web3Wrapper.awaitTransactionSuccessAsync( - await tokenContract.setBalance.sendTransactionAsync(userAddress, amount, { - from: this._contractOwnerAddress, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - } - public async getProxyAllowanceAsync(userAddress: string, assetData: string): Promise<BigNumber> { - const tokenContract = this._getTokenContractFromAssetData(assetData); - const proxyAddress = (this._proxyContract as ERC20ProxyContract).address; - const allowance = new BigNumber(await tokenContract.allowance.callAsync(userAddress, proxyAddress)); - return allowance; - } - public async setAllowanceAsync(userAddress: string, assetData: string, amount: BigNumber): Promise<void> { - const tokenContract = this._getTokenContractFromAssetData(assetData); - const proxyAddress = (this._proxyContract as ERC20ProxyContract).address; - await this._web3Wrapper.awaitTransactionSuccessAsync( - await tokenContract.approve.sendTransactionAsync(proxyAddress, amount, { - from: userAddress, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - } - public async getBalancesAsync(): Promise<ERC20BalancesByOwner> { - this._validateDummyTokenContractsExistOrThrow(); - const balancesByOwner: ERC20BalancesByOwner = {}; - const balances: BigNumber[] = []; - const balanceInfo: Array<{ tokenOwnerAddress: string; tokenAddress: string }> = []; - for (const dummyTokenContract of this._dummyTokenContracts) { - for (const tokenOwnerAddress of this._tokenOwnerAddresses) { - balances.push(await dummyTokenContract.balanceOf.callAsync(tokenOwnerAddress)); - balanceInfo.push({ - tokenOwnerAddress, - tokenAddress: dummyTokenContract.address, - }); - } - } - _.forEach(balances, (balance, balanceIndex) => { - const tokenAddress = balanceInfo[balanceIndex].tokenAddress; - const tokenOwnerAddress = balanceInfo[balanceIndex].tokenOwnerAddress; - if (_.isUndefined(balancesByOwner[tokenOwnerAddress])) { - balancesByOwner[tokenOwnerAddress] = {}; - } - const wrappedBalance = new BigNumber(balance); - balancesByOwner[tokenOwnerAddress][tokenAddress] = wrappedBalance; - }); - return balancesByOwner; - } - public addDummyTokenContract(dummy: DummyERC20TokenContract): void { - if (!_.isUndefined(this._dummyTokenContracts)) { - this._dummyTokenContracts.push(dummy); - } - } - public addTokenOwnerAddress(address: string): void { - this._tokenOwnerAddresses.push(address); - } - public getTokenOwnerAddresses(): string[] { - return this._tokenOwnerAddresses; - } - public getTokenAddresses(): string[] { - const tokenAddresses = _.map(this._dummyTokenContracts, dummyTokenContract => dummyTokenContract.address); - return tokenAddresses; - } - private _getTokenContractFromAssetData(assetData: string): DummyERC20TokenContract { - const erc20ProxyData = assetDataUtils.decodeERC20AssetData(assetData); - const tokenAddress = erc20ProxyData.tokenAddress; - const tokenContractIfExists = _.find(this._dummyTokenContracts, c => c.address === tokenAddress); - if (_.isUndefined(tokenContractIfExists)) { - throw new Error(`Token: ${tokenAddress} was not deployed through ERC20Wrapper`); - } - return tokenContractIfExists; - } - private _validateDummyTokenContractsExistOrThrow(): void { - if (_.isUndefined(this._dummyTokenContracts)) { - throw new Error('Dummy ERC20 tokens not yet deployed, please call "deployDummyTokensAsync"'); - } - } - private _validateProxyContractExistsOrThrow(): void { - if (_.isUndefined(this._proxyContract)) { - throw new Error('ERC20 proxy contract not yet deployed, please call "deployProxyAsync"'); - } - } -} diff --git a/contracts/core/test/utils/erc721_wrapper.ts b/contracts/core/test/utils/erc721_wrapper.ts deleted file mode 100644 index 1f658ce86..000000000 --- a/contracts/core/test/utils/erc721_wrapper.ts +++ /dev/null @@ -1,236 +0,0 @@ -import { constants, ERC721TokenIdsByOwner, txDefaults } from '@0x/contracts-test-utils'; -import { artifacts as tokensArtifacts, DummyERC721TokenContract } from '@0x/contracts-tokens'; -import { generatePseudoRandomSalt } from '@0x/order-utils'; -import { BigNumber } from '@0x/utils'; -import { Web3Wrapper } from '@0x/web3-wrapper'; -import { Provider } from 'ethereum-types'; -import * as _ from 'lodash'; - -import { ERC721ProxyContract } from '../../generated-wrappers/erc721_proxy'; -import { artifacts } from '../../src/artifacts'; - -export class ERC721Wrapper { - private readonly _tokenOwnerAddresses: string[]; - private readonly _contractOwnerAddress: string; - private readonly _web3Wrapper: Web3Wrapper; - private readonly _provider: Provider; - private readonly _dummyTokenContracts: DummyERC721TokenContract[]; - private _proxyContract?: ERC721ProxyContract; - private _proxyIdIfExists?: string; - private _initialTokenIdsByOwner: ERC721TokenIdsByOwner = {}; - constructor(provider: Provider, tokenOwnerAddresses: string[], contractOwnerAddress: string) { - this._web3Wrapper = new Web3Wrapper(provider); - this._provider = provider; - this._dummyTokenContracts = []; - this._tokenOwnerAddresses = tokenOwnerAddresses; - this._contractOwnerAddress = contractOwnerAddress; - } - public async deployDummyTokensAsync(): Promise<DummyERC721TokenContract[]> { - // tslint:disable-next-line:no-unused-variable - for (const i of _.times(constants.NUM_DUMMY_ERC721_TO_DEPLOY)) { - this._dummyTokenContracts.push( - await DummyERC721TokenContract.deployFrom0xArtifactAsync( - tokensArtifacts.DummyERC721Token, - this._provider, - txDefaults, - constants.DUMMY_TOKEN_NAME, - constants.DUMMY_TOKEN_SYMBOL, - ), - ); - } - return this._dummyTokenContracts; - } - public async deployProxyAsync(): Promise<ERC721ProxyContract> { - this._proxyContract = await ERC721ProxyContract.deployFrom0xArtifactAsync( - artifacts.ERC721Proxy, - this._provider, - txDefaults, - ); - this._proxyIdIfExists = await this._proxyContract.getProxyId.callAsync(); - return this._proxyContract; - } - public getProxyId(): string { - this._validateProxyContractExistsOrThrow(); - return this._proxyIdIfExists as string; - } - public async setBalancesAndAllowancesAsync(): Promise<void> { - this._validateDummyTokenContractsExistOrThrow(); - this._validateProxyContractExistsOrThrow(); - this._initialTokenIdsByOwner = {}; - for (const dummyTokenContract of this._dummyTokenContracts) { - for (const tokenOwnerAddress of this._tokenOwnerAddresses) { - // tslint:disable-next-line:no-unused-variable - for (const i of _.times(constants.NUM_ERC721_TOKENS_TO_MINT)) { - const tokenId = generatePseudoRandomSalt(); - await this.mintAsync(dummyTokenContract.address, tokenId, tokenOwnerAddress); - if (_.isUndefined(this._initialTokenIdsByOwner[tokenOwnerAddress])) { - this._initialTokenIdsByOwner[tokenOwnerAddress] = { - [dummyTokenContract.address]: [], - }; - } - if (_.isUndefined(this._initialTokenIdsByOwner[tokenOwnerAddress][dummyTokenContract.address])) { - this._initialTokenIdsByOwner[tokenOwnerAddress][dummyTokenContract.address] = []; - } - this._initialTokenIdsByOwner[tokenOwnerAddress][dummyTokenContract.address].push(tokenId); - - await this.approveProxyAsync(dummyTokenContract.address, tokenId); - } - } - } - } - public async doesTokenExistAsync(tokenAddress: string, tokenId: BigNumber): Promise<boolean> { - const tokenContract = this._getTokenContractFromAssetData(tokenAddress); - const owner = await tokenContract.ownerOf.callAsync(tokenId); - const doesExist = owner !== constants.NULL_ADDRESS; - return doesExist; - } - public async approveProxyAsync(tokenAddress: string, tokenId: BigNumber): Promise<void> { - const proxyAddress = (this._proxyContract as ERC721ProxyContract).address; - await this.approveAsync(proxyAddress, tokenAddress, tokenId); - } - public async approveProxyForAllAsync(tokenAddress: string, tokenId: BigNumber, isApproved: boolean): Promise<void> { - const tokenContract = this._getTokenContractFromAssetData(tokenAddress); - const tokenOwner = await this.ownerOfAsync(tokenAddress, tokenId); - const proxyAddress = (this._proxyContract as ERC721ProxyContract).address; - await this._web3Wrapper.awaitTransactionSuccessAsync( - await tokenContract.setApprovalForAll.sendTransactionAsync(proxyAddress, isApproved, { - from: tokenOwner, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - } - public async approveAsync(to: string, tokenAddress: string, tokenId: BigNumber): Promise<void> { - const tokenContract = this._getTokenContractFromAssetData(tokenAddress); - const tokenOwner = await this.ownerOfAsync(tokenAddress, tokenId); - await this._web3Wrapper.awaitTransactionSuccessAsync( - await tokenContract.approve.sendTransactionAsync(to, tokenId, { - from: tokenOwner, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - } - public async transferFromAsync( - tokenAddress: string, - tokenId: BigNumber, - currentOwner: string, - userAddress: string, - ): Promise<void> { - const tokenContract = this._getTokenContractFromAssetData(tokenAddress); - await this._web3Wrapper.awaitTransactionSuccessAsync( - await tokenContract.transferFrom.sendTransactionAsync(currentOwner, userAddress, tokenId, { - from: currentOwner, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - } - public async mintAsync(tokenAddress: string, tokenId: BigNumber, userAddress: string): Promise<void> { - const tokenContract = this._getTokenContractFromAssetData(tokenAddress); - await this._web3Wrapper.awaitTransactionSuccessAsync( - await tokenContract.mint.sendTransactionAsync(userAddress, tokenId, { - from: this._contractOwnerAddress, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - } - public async burnAsync(tokenAddress: string, tokenId: BigNumber, owner: string): Promise<void> { - const tokenContract = this._getTokenContractFromAssetData(tokenAddress); - await this._web3Wrapper.awaitTransactionSuccessAsync( - await tokenContract.burn.sendTransactionAsync(owner, tokenId, { - from: this._contractOwnerAddress, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - } - public async ownerOfAsync(tokenAddress: string, tokenId: BigNumber): Promise<string> { - const tokenContract = this._getTokenContractFromAssetData(tokenAddress); - const owner = await tokenContract.ownerOf.callAsync(tokenId); - return owner; - } - public async isOwnerAsync(userAddress: string, tokenAddress: string, tokenId: BigNumber): Promise<boolean> { - const tokenContract = this._getTokenContractFromAssetData(tokenAddress); - const tokenOwner = await tokenContract.ownerOf.callAsync(tokenId); - const isOwner = tokenOwner === userAddress; - return isOwner; - } - public async isProxyApprovedForAllAsync(userAddress: string, tokenAddress: string): Promise<boolean> { - this._validateProxyContractExistsOrThrow(); - const tokenContract = this._getTokenContractFromAssetData(tokenAddress); - const operator = (this._proxyContract as ERC721ProxyContract).address; - const didApproveAll = await tokenContract.isApprovedForAll.callAsync(userAddress, operator); - return didApproveAll; - } - public async isProxyApprovedAsync(tokenAddress: string, tokenId: BigNumber): Promise<boolean> { - this._validateProxyContractExistsOrThrow(); - const tokenContract = this._getTokenContractFromAssetData(tokenAddress); - const approvedAddress = await tokenContract.getApproved.callAsync(tokenId); - const proxyAddress = (this._proxyContract as ERC721ProxyContract).address; - const isProxyAnApprovedOperator = approvedAddress === proxyAddress; - return isProxyAnApprovedOperator; - } - public async getBalancesAsync(): Promise<ERC721TokenIdsByOwner> { - this._validateDummyTokenContractsExistOrThrow(); - this._validateBalancesAndAllowancesSetOrThrow(); - const tokenIdsByOwner: ERC721TokenIdsByOwner = {}; - const tokenOwnerAddresses: string[] = []; - const tokenInfo: Array<{ tokenId: BigNumber; tokenAddress: string }> = []; - for (const dummyTokenContract of this._dummyTokenContracts) { - for (const tokenOwnerAddress of this._tokenOwnerAddresses) { - const initialTokenOwnerIds = this._initialTokenIdsByOwner[tokenOwnerAddress][ - dummyTokenContract.address - ]; - for (const tokenId of initialTokenOwnerIds) { - tokenOwnerAddresses.push(await dummyTokenContract.ownerOf.callAsync(tokenId)); - tokenInfo.push({ - tokenId, - tokenAddress: dummyTokenContract.address, - }); - } - } - } - _.forEach(tokenOwnerAddresses, (tokenOwnerAddress, ownerIndex) => { - const tokenAddress = tokenInfo[ownerIndex].tokenAddress; - const tokenId = tokenInfo[ownerIndex].tokenId; - if (_.isUndefined(tokenIdsByOwner[tokenOwnerAddress])) { - tokenIdsByOwner[tokenOwnerAddress] = { - [tokenAddress]: [], - }; - } - if (_.isUndefined(tokenIdsByOwner[tokenOwnerAddress][tokenAddress])) { - tokenIdsByOwner[tokenOwnerAddress][tokenAddress] = []; - } - tokenIdsByOwner[tokenOwnerAddress][tokenAddress].push(tokenId); - }); - return tokenIdsByOwner; - } - public getTokenOwnerAddresses(): string[] { - return this._tokenOwnerAddresses; - } - public getTokenAddresses(): string[] { - const tokenAddresses = _.map(this._dummyTokenContracts, dummyTokenContract => dummyTokenContract.address); - return tokenAddresses; - } - private _getTokenContractFromAssetData(tokenAddress: string): DummyERC721TokenContract { - const tokenContractIfExists = _.find(this._dummyTokenContracts, c => c.address === tokenAddress); - if (_.isUndefined(tokenContractIfExists)) { - throw new Error(`Token: ${tokenAddress} was not deployed through ERC20Wrapper`); - } - return tokenContractIfExists; - } - private _validateDummyTokenContractsExistOrThrow(): void { - if (_.isUndefined(this._dummyTokenContracts)) { - throw new Error('Dummy ERC721 tokens not yet deployed, please call "deployDummyTokensAsync"'); - } - } - private _validateProxyContractExistsOrThrow(): void { - if (_.isUndefined(this._proxyContract)) { - throw new Error('ERC721 proxy contract not yet deployed, please call "deployProxyAsync"'); - } - } - private _validateBalancesAndAllowancesSetOrThrow(): void { - if (_.keys(this._initialTokenIdsByOwner).length === 0) { - throw new Error( - 'Dummy ERC721 balances and allowances not yet set, please call "setBalancesAndAllowancesAsync"', - ); - } - } -} diff --git a/contracts/core/test/utils/exchange_wrapper.ts b/contracts/core/test/utils/exchange_wrapper.ts deleted file mode 100644 index 075d2cb96..000000000 --- a/contracts/core/test/utils/exchange_wrapper.ts +++ /dev/null @@ -1,281 +0,0 @@ -import { - FillResults, - formatters, - LogDecoder, - OrderInfo, - orderUtils, - SignedTransaction, -} from '@0x/contracts-test-utils'; -import { artifacts as tokensArtifacts } from '@0x/contracts-tokens'; -import { SignedOrder } from '@0x/types'; -import { BigNumber } from '@0x/utils'; -import { Web3Wrapper } from '@0x/web3-wrapper'; -import { Provider, TransactionReceiptWithDecodedLogs } from 'ethereum-types'; - -import { ExchangeContract } from '../../generated-wrappers/exchange'; -import { artifacts } from '../../src/artifacts'; - -export class ExchangeWrapper { - private readonly _exchange: ExchangeContract; - private readonly _web3Wrapper: Web3Wrapper; - private readonly _logDecoder: LogDecoder; - constructor(exchangeContract: ExchangeContract, provider: Provider) { - this._exchange = exchangeContract; - this._web3Wrapper = new Web3Wrapper(provider); - this._logDecoder = new LogDecoder(this._web3Wrapper, { ...artifacts, ...tokensArtifacts }); - } - public async fillOrderAsync( - signedOrder: SignedOrder, - from: string, - opts: { takerAssetFillAmount?: BigNumber } = {}, - ): Promise<TransactionReceiptWithDecodedLogs> { - const params = orderUtils.createFill(signedOrder, opts.takerAssetFillAmount); - const txHash = await this._exchange.fillOrder.sendTransactionAsync( - params.order, - params.takerAssetFillAmount, - params.signature, - { from }, - ); - const txReceipt = await this._logDecoder.getTxWithDecodedLogsAsync(txHash); - return txReceipt; - } - public async cancelOrderAsync(signedOrder: SignedOrder, from: string): Promise<TransactionReceiptWithDecodedLogs> { - const params = orderUtils.createCancel(signedOrder); - const txHash = await this._exchange.cancelOrder.sendTransactionAsync(params.order, { from }); - const tx = await this._logDecoder.getTxWithDecodedLogsAsync(txHash); - return tx; - } - public async fillOrKillOrderAsync( - signedOrder: SignedOrder, - from: string, - opts: { takerAssetFillAmount?: BigNumber } = {}, - ): Promise<TransactionReceiptWithDecodedLogs> { - const params = orderUtils.createFill(signedOrder, opts.takerAssetFillAmount); - const txHash = await this._exchange.fillOrKillOrder.sendTransactionAsync( - params.order, - params.takerAssetFillAmount, - params.signature, - { from }, - ); - const tx = await this._logDecoder.getTxWithDecodedLogsAsync(txHash); - return tx; - } - public async fillOrderNoThrowAsync( - signedOrder: SignedOrder, - from: string, - opts: { takerAssetFillAmount?: BigNumber; gas?: number } = {}, - ): Promise<TransactionReceiptWithDecodedLogs> { - const params = orderUtils.createFill(signedOrder, opts.takerAssetFillAmount); - const txHash = await this._exchange.fillOrderNoThrow.sendTransactionAsync( - params.order, - params.takerAssetFillAmount, - params.signature, - { from, gas: opts.gas }, - ); - const tx = await this._logDecoder.getTxWithDecodedLogsAsync(txHash); - return tx; - } - public async batchFillOrdersAsync( - orders: SignedOrder[], - from: string, - opts: { takerAssetFillAmounts?: BigNumber[] } = {}, - ): Promise<TransactionReceiptWithDecodedLogs> { - const params = formatters.createBatchFill(orders, opts.takerAssetFillAmounts); - const txHash = await this._exchange.batchFillOrders.sendTransactionAsync( - params.orders, - params.takerAssetFillAmounts, - params.signatures, - { from }, - ); - const tx = await this._logDecoder.getTxWithDecodedLogsAsync(txHash); - return tx; - } - public async batchFillOrKillOrdersAsync( - orders: SignedOrder[], - from: string, - opts: { takerAssetFillAmounts?: BigNumber[] } = {}, - ): Promise<TransactionReceiptWithDecodedLogs> { - const params = formatters.createBatchFill(orders, opts.takerAssetFillAmounts); - const txHash = await this._exchange.batchFillOrKillOrders.sendTransactionAsync( - params.orders, - params.takerAssetFillAmounts, - params.signatures, - { from }, - ); - const tx = await this._logDecoder.getTxWithDecodedLogsAsync(txHash); - return tx; - } - public async batchFillOrdersNoThrowAsync( - orders: SignedOrder[], - from: string, - opts: { takerAssetFillAmounts?: BigNumber[]; gas?: number } = {}, - ): Promise<TransactionReceiptWithDecodedLogs> { - const params = formatters.createBatchFill(orders, opts.takerAssetFillAmounts); - const txHash = await this._exchange.batchFillOrdersNoThrow.sendTransactionAsync( - params.orders, - params.takerAssetFillAmounts, - params.signatures, - { from, gas: opts.gas }, - ); - const tx = await this._logDecoder.getTxWithDecodedLogsAsync(txHash); - return tx; - } - public async marketSellOrdersAsync( - orders: SignedOrder[], - from: string, - opts: { takerAssetFillAmount: BigNumber }, - ): Promise<TransactionReceiptWithDecodedLogs> { - const params = formatters.createMarketSellOrders(orders, opts.takerAssetFillAmount); - const txHash = await this._exchange.marketSellOrders.sendTransactionAsync( - params.orders, - params.takerAssetFillAmount, - params.signatures, - { from }, - ); - const tx = await this._logDecoder.getTxWithDecodedLogsAsync(txHash); - return tx; - } - public async marketSellOrdersNoThrowAsync( - orders: SignedOrder[], - from: string, - opts: { takerAssetFillAmount: BigNumber; gas?: number }, - ): Promise<TransactionReceiptWithDecodedLogs> { - const params = formatters.createMarketSellOrders(orders, opts.takerAssetFillAmount); - const txHash = await this._exchange.marketSellOrdersNoThrow.sendTransactionAsync( - params.orders, - params.takerAssetFillAmount, - params.signatures, - { from, gas: opts.gas }, - ); - const tx = await this._logDecoder.getTxWithDecodedLogsAsync(txHash); - return tx; - } - public async marketBuyOrdersAsync( - orders: SignedOrder[], - from: string, - opts: { makerAssetFillAmount: BigNumber }, - ): Promise<TransactionReceiptWithDecodedLogs> { - const params = formatters.createMarketBuyOrders(orders, opts.makerAssetFillAmount); - const txHash = await this._exchange.marketBuyOrders.sendTransactionAsync( - params.orders, - params.makerAssetFillAmount, - params.signatures, - { from }, - ); - const tx = await this._logDecoder.getTxWithDecodedLogsAsync(txHash); - return tx; - } - public async marketBuyOrdersNoThrowAsync( - orders: SignedOrder[], - from: string, - opts: { makerAssetFillAmount: BigNumber; gas?: number }, - ): Promise<TransactionReceiptWithDecodedLogs> { - const params = formatters.createMarketBuyOrders(orders, opts.makerAssetFillAmount); - const txHash = await this._exchange.marketBuyOrdersNoThrow.sendTransactionAsync( - params.orders, - params.makerAssetFillAmount, - params.signatures, - { from, gas: opts.gas }, - ); - const tx = await this._logDecoder.getTxWithDecodedLogsAsync(txHash); - return tx; - } - public async batchCancelOrdersAsync( - orders: SignedOrder[], - from: string, - ): Promise<TransactionReceiptWithDecodedLogs> { - const params = formatters.createBatchCancel(orders); - const txHash = await this._exchange.batchCancelOrders.sendTransactionAsync(params.orders, { from }); - const tx = await this._logDecoder.getTxWithDecodedLogsAsync(txHash); - return tx; - } - public async cancelOrdersUpToAsync(salt: BigNumber, from: string): Promise<TransactionReceiptWithDecodedLogs> { - const txHash = await this._exchange.cancelOrdersUpTo.sendTransactionAsync(salt, { from }); - const tx = await this._logDecoder.getTxWithDecodedLogsAsync(txHash); - return tx; - } - public async registerAssetProxyAsync( - assetProxyAddress: string, - from: string, - ): Promise<TransactionReceiptWithDecodedLogs> { - const txHash = await this._exchange.registerAssetProxy.sendTransactionAsync(assetProxyAddress, { from }); - const tx = await this._logDecoder.getTxWithDecodedLogsAsync(txHash); - return tx; - } - public async executeTransactionAsync( - signedTx: SignedTransaction, - from: string, - ): Promise<TransactionReceiptWithDecodedLogs> { - const txHash = await this._exchange.executeTransaction.sendTransactionAsync( - signedTx.salt, - signedTx.signerAddress, - signedTx.data, - signedTx.signature, - { from }, - ); - const tx = await this._logDecoder.getTxWithDecodedLogsAsync(txHash); - return tx; - } - public async getTakerAssetFilledAmountAsync(orderHashHex: string): Promise<BigNumber> { - const filledAmount = await this._exchange.filled.callAsync(orderHashHex); - return filledAmount; - } - public async isCancelledAsync(orderHashHex: string): Promise<boolean> { - const isCancelled = await this._exchange.cancelled.callAsync(orderHashHex); - return isCancelled; - } - public async getOrderEpochAsync(makerAddress: string, senderAddress: string): Promise<BigNumber> { - const orderEpoch = await this._exchange.orderEpoch.callAsync(makerAddress, senderAddress); - return orderEpoch; - } - public async getOrderInfoAsync(signedOrder: SignedOrder): Promise<OrderInfo> { - const orderInfo = (await this._exchange.getOrderInfo.callAsync(signedOrder)) as OrderInfo; - return orderInfo; - } - public async getOrdersInfoAsync(signedOrders: SignedOrder[]): Promise<OrderInfo[]> { - const ordersInfo = (await this._exchange.getOrdersInfo.callAsync(signedOrders)) as OrderInfo[]; - return ordersInfo; - } - public async matchOrdersAsync( - signedOrderLeft: SignedOrder, - signedOrderRight: SignedOrder, - from: string, - ): Promise<TransactionReceiptWithDecodedLogs> { - const params = orderUtils.createMatchOrders(signedOrderLeft, signedOrderRight); - const txHash = await this._exchange.matchOrders.sendTransactionAsync( - params.left, - params.right, - params.leftSignature, - params.rightSignature, - { from }, - ); - const tx = await this._logDecoder.getTxWithDecodedLogsAsync(txHash); - return tx; - } - public async getFillOrderResultsAsync( - signedOrder: SignedOrder, - from: string, - opts: { takerAssetFillAmount?: BigNumber } = {}, - ): Promise<FillResults> { - const params = orderUtils.createFill(signedOrder, opts.takerAssetFillAmount); - const fillResults = await this._exchange.fillOrder.callAsync( - params.order, - params.takerAssetFillAmount, - params.signature, - { from }, - ); - return fillResults; - } - public abiEncodeFillOrder(signedOrder: SignedOrder, opts: { takerAssetFillAmount?: BigNumber } = {}): string { - const params = orderUtils.createFill(signedOrder, opts.takerAssetFillAmount); - const data = this._exchange.fillOrder.getABIEncodedTransactionData( - params.order, - params.takerAssetFillAmount, - params.signature, - ); - return data; - } - public getExchangeAddress(): string { - return this._exchange.address; - } -} diff --git a/contracts/core/test/utils/fill_order_combinatorial_utils.ts b/contracts/core/test/utils/fill_order_combinatorial_utils.ts deleted file mode 100644 index 5d0ea07a8..000000000 --- a/contracts/core/test/utils/fill_order_combinatorial_utils.ts +++ /dev/null @@ -1,928 +0,0 @@ -import { artifacts as libsArtifacts, TestLibsContract } from '@0x/contracts-libs'; -import { - AllowanceAmountScenario, - AssetDataScenario, - BalanceAmountScenario, - chaiSetup, - constants, - expectTransactionFailedAsync, - ExpirationTimeSecondsScenario, - FeeRecipientAddressScenario, - FillScenario, - OrderAssetAmountScenario, - orderUtils, - signingUtils, - TakerAssetFillAmountScenario, - TakerScenario, - TraderStateScenario, -} from '@0x/contracts-test-utils'; -import { - assetDataUtils, - BalanceAndProxyAllowanceLazyStore, - ExchangeTransferSimulator, - orderHashUtils, - OrderStateUtils, - OrderValidationUtils, -} from '@0x/order-utils'; -import { AssetProxyId, RevertReason, SignatureType, SignedOrder } from '@0x/types'; -import { BigNumber, errorUtils, logUtils } from '@0x/utils'; -import { Web3Wrapper } from '@0x/web3-wrapper'; -import * as chai from 'chai'; -import { LogWithDecodedArgs, Provider, TxData } from 'ethereum-types'; -import * as _ from 'lodash'; -import 'make-promises-safe'; - -import { ExchangeContract, ExchangeFillEventArgs } from '../../generated-wrappers/exchange'; -import { artifacts } from '../../src/artifacts'; - -import { AssetWrapper } from './asset_wrapper'; -import { ERC20Wrapper } from './erc20_wrapper'; -import { ERC721Wrapper } from './erc721_wrapper'; -import { ExchangeWrapper } from './exchange_wrapper'; -import { OrderFactoryFromScenario } from './order_factory_from_scenario'; -import { SimpleAssetBalanceAndProxyAllowanceFetcher } from './simple_asset_balance_and_proxy_allowance_fetcher'; -import { SimpleOrderFilledCancelledFetcher } from './simple_order_filled_cancelled_fetcher'; - -chaiSetup.configure(); -const expect = chai.expect; - -/** - * Instantiates a new instance of FillOrderCombinatorialUtils. Since this method has some - * required async setup, a factory method is required. - * @param web3Wrapper Web3Wrapper instance - * @param txDefaults Default Ethereum tx options - * @return FillOrderCombinatorialUtils instance - */ -export async function fillOrderCombinatorialUtilsFactoryAsync( - web3Wrapper: Web3Wrapper, - txDefaults: Partial<TxData>, -): Promise<FillOrderCombinatorialUtils> { - const accounts = await web3Wrapper.getAvailableAddressesAsync(); - const userAddresses = _.slice(accounts, 0, 5); - const [ownerAddress, makerAddress, takerAddress] = userAddresses; - const makerPrivateKey = constants.TESTRPC_PRIVATE_KEYS[userAddresses.indexOf(makerAddress)]; - - const provider = web3Wrapper.getProvider(); - const erc20Wrapper = new ERC20Wrapper(provider, userAddresses, ownerAddress); - const erc721Wrapper = new ERC721Wrapper(provider, userAddresses, ownerAddress); - - const erc20EighteenDecimalTokenCount = 3; - const eighteenDecimals = new BigNumber(18); - const [ - erc20EighteenDecimalTokenA, - erc20EighteenDecimalTokenB, - zrxToken, - ] = await erc20Wrapper.deployDummyTokensAsync(erc20EighteenDecimalTokenCount, eighteenDecimals); - const zrxAssetData = assetDataUtils.encodeERC20AssetData(zrxToken.address); - - const erc20FiveDecimalTokenCount = 2; - const fiveDecimals = new BigNumber(5); - const [erc20FiveDecimalTokenA, erc20FiveDecimalTokenB] = await erc20Wrapper.deployDummyTokensAsync( - erc20FiveDecimalTokenCount, - fiveDecimals, - ); - const zeroDecimals = new BigNumber(0); - const erc20ZeroDecimalTokenCount = 2; - const [erc20ZeroDecimalTokenA, erc20ZeroDecimalTokenB] = await erc20Wrapper.deployDummyTokensAsync( - erc20ZeroDecimalTokenCount, - zeroDecimals, - ); - const erc20Proxy = await erc20Wrapper.deployProxyAsync(); - await erc20Wrapper.setBalancesAndAllowancesAsync(); - - const [erc721Token] = await erc721Wrapper.deployDummyTokensAsync(); - const erc721Proxy = await erc721Wrapper.deployProxyAsync(); - await erc721Wrapper.setBalancesAndAllowancesAsync(); - const erc721Balances = await erc721Wrapper.getBalancesAsync(); - - const assetWrapper = new AssetWrapper([erc20Wrapper, erc721Wrapper]); - - const exchangeContract = await ExchangeContract.deployFrom0xArtifactAsync( - artifacts.Exchange, - provider, - txDefaults, - zrxAssetData, - ); - const exchangeWrapper = new ExchangeWrapper(exchangeContract, provider); - await exchangeWrapper.registerAssetProxyAsync(erc20Proxy.address, ownerAddress); - await exchangeWrapper.registerAssetProxyAsync(erc721Proxy.address, ownerAddress); - - await web3Wrapper.awaitTransactionSuccessAsync( - await erc20Proxy.addAuthorizedAddress.sendTransactionAsync(exchangeContract.address, { - from: ownerAddress, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - await web3Wrapper.awaitTransactionSuccessAsync( - await erc721Proxy.addAuthorizedAddress.sendTransactionAsync(exchangeContract.address, { - from: ownerAddress, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - - const orderFactory = new OrderFactoryFromScenario( - userAddresses, - zrxToken.address, - [erc20EighteenDecimalTokenA.address, erc20EighteenDecimalTokenB.address], - [erc20FiveDecimalTokenA.address, erc20FiveDecimalTokenB.address], - [erc20ZeroDecimalTokenA.address, erc20ZeroDecimalTokenB.address], - erc721Token, - erc721Balances, - exchangeContract.address, - ); - - const testLibsContract = await TestLibsContract.deployFrom0xArtifactAsync( - libsArtifacts.TestLibs, - provider, - txDefaults, - ); - - const fillOrderCombinatorialUtils = new FillOrderCombinatorialUtils( - orderFactory, - ownerAddress, - makerAddress, - makerPrivateKey, - takerAddress, - zrxAssetData, - exchangeWrapper, - assetWrapper, - testLibsContract, - ); - return fillOrderCombinatorialUtils; -} - -export class FillOrderCombinatorialUtils { - public orderFactory: OrderFactoryFromScenario; - public ownerAddress: string; - public makerAddress: string; - public makerPrivateKey: Buffer; - public takerAddress: string; - public zrxAssetData: string; - public exchangeWrapper: ExchangeWrapper; - public assetWrapper: AssetWrapper; - public testLibsContract: TestLibsContract; - public static generateFillOrderCombinations(): FillScenario[] { - const takerScenarios = [ - TakerScenario.Unspecified, - // TakerScenario.CorrectlySpecified, - // TakerScenario.IncorrectlySpecified, - ]; - const feeRecipientScenarios = [ - FeeRecipientAddressScenario.EthUserAddress, - // FeeRecipientAddressScenario.BurnAddress, - ]; - const makerAssetAmountScenario = [ - OrderAssetAmountScenario.Large, - // OrderAssetAmountScenario.Zero, - // OrderAssetAmountScenario.Small, - ]; - const takerAssetAmountScenario = [ - OrderAssetAmountScenario.Large, - // OrderAssetAmountScenario.Zero, - // OrderAssetAmountScenario.Small, - ]; - const makerFeeScenario = [ - OrderAssetAmountScenario.Large, - // OrderAssetAmountScenario.Small, - // OrderAssetAmountScenario.Zero, - ]; - const takerFeeScenario = [ - OrderAssetAmountScenario.Large, - // OrderAssetAmountScenario.Small, - // OrderAssetAmountScenario.Zero, - ]; - const expirationTimeSecondsScenario = [ - ExpirationTimeSecondsScenario.InFuture, - ExpirationTimeSecondsScenario.InPast, - ]; - const makerAssetDataScenario = [ - AssetDataScenario.ERC20FiveDecimals, - AssetDataScenario.ERC20NonZRXEighteenDecimals, - AssetDataScenario.ERC721, - AssetDataScenario.ZRXFeeToken, - ]; - const takerAssetDataScenario = [ - AssetDataScenario.ERC20FiveDecimals, - AssetDataScenario.ERC20NonZRXEighteenDecimals, - AssetDataScenario.ERC721, - AssetDataScenario.ZRXFeeToken, - ]; - const takerAssetFillAmountScenario = [ - TakerAssetFillAmountScenario.ExactlyRemainingFillableTakerAssetAmount, - // TakerAssetFillAmountScenario.GreaterThanRemainingFillableTakerAssetAmount, - // TakerAssetFillAmountScenario.LessThanRemainingFillableTakerAssetAmount, - ]; - const makerAssetBalanceScenario = [ - BalanceAmountScenario.Higher, - // BalanceAmountScenario.Exact, - // BalanceAmountScenario.TooLow, - ]; - const makerAssetAllowanceScenario = [ - AllowanceAmountScenario.Higher, - // AllowanceAmountScenario.Exact, - // AllowanceAmountScenario.TooLow, - // AllowanceAmountScenario.Unlimited, - ]; - const makerZRXBalanceScenario = [ - BalanceAmountScenario.Higher, - // BalanceAmountScenario.Exact, - // BalanceAmountScenario.TooLow, - ]; - const makerZRXAllowanceScenario = [ - AllowanceAmountScenario.Higher, - // AllowanceAmountScenario.Exact, - // AllowanceAmountScenario.TooLow, - // AllowanceAmountScenario.Unlimited, - ]; - const takerAssetBalanceScenario = [ - BalanceAmountScenario.Higher, - // BalanceAmountScenario.Exact, - // BalanceAmountScenario.TooLow, - ]; - const takerAssetAllowanceScenario = [ - AllowanceAmountScenario.Higher, - // AllowanceAmountScenario.Exact, - // AllowanceAmountScenario.TooLow, - // AllowanceAmountScenario.Unlimited, - ]; - const takerZRXBalanceScenario = [ - BalanceAmountScenario.Higher, - // BalanceAmountScenario.Exact, - // BalanceAmountScenario.TooLow, - ]; - const takerZRXAllowanceScenario = [ - AllowanceAmountScenario.Higher, - // AllowanceAmountScenario.Exact, - // AllowanceAmountScenario.TooLow, - // AllowanceAmountScenario.Unlimited, - ]; - const fillScenarioArrays = FillOrderCombinatorialUtils._getAllCombinations([ - takerScenarios, - feeRecipientScenarios, - makerAssetAmountScenario, - takerAssetAmountScenario, - makerFeeScenario, - takerFeeScenario, - expirationTimeSecondsScenario, - makerAssetDataScenario, - takerAssetDataScenario, - takerAssetFillAmountScenario, - makerAssetBalanceScenario, - makerAssetAllowanceScenario, - makerZRXBalanceScenario, - makerZRXAllowanceScenario, - takerAssetBalanceScenario, - takerAssetAllowanceScenario, - takerZRXBalanceScenario, - takerZRXAllowanceScenario, - ]); - - const fillScenarios = _.map(fillScenarioArrays, fillScenarioArray => { - // tslint:disable:custom-no-magic-numbers - const fillScenario: FillScenario = { - orderScenario: { - takerScenario: fillScenarioArray[0] as TakerScenario, - feeRecipientScenario: fillScenarioArray[1] as FeeRecipientAddressScenario, - makerAssetAmountScenario: fillScenarioArray[2] as OrderAssetAmountScenario, - takerAssetAmountScenario: fillScenarioArray[3] as OrderAssetAmountScenario, - makerFeeScenario: fillScenarioArray[4] as OrderAssetAmountScenario, - takerFeeScenario: fillScenarioArray[5] as OrderAssetAmountScenario, - expirationTimeSecondsScenario: fillScenarioArray[6] as ExpirationTimeSecondsScenario, - makerAssetDataScenario: fillScenarioArray[7] as AssetDataScenario, - takerAssetDataScenario: fillScenarioArray[8] as AssetDataScenario, - }, - takerAssetFillAmountScenario: fillScenarioArray[9] as TakerAssetFillAmountScenario, - makerStateScenario: { - traderAssetBalance: fillScenarioArray[10] as BalanceAmountScenario, - traderAssetAllowance: fillScenarioArray[11] as AllowanceAmountScenario, - zrxFeeBalance: fillScenarioArray[12] as BalanceAmountScenario, - zrxFeeAllowance: fillScenarioArray[13] as AllowanceAmountScenario, - }, - takerStateScenario: { - traderAssetBalance: fillScenarioArray[14] as BalanceAmountScenario, - traderAssetAllowance: fillScenarioArray[15] as AllowanceAmountScenario, - zrxFeeBalance: fillScenarioArray[16] as BalanceAmountScenario, - zrxFeeAllowance: fillScenarioArray[17] as AllowanceAmountScenario, - }, - }; - // tslint:enable:custom-no-magic-numbers - return fillScenario; - }); - - return fillScenarios; - } - /** - * Recursive implementation of generating all combinations of the supplied - * string-containing arrays. - */ - private static _getAllCombinations(arrays: string[][]): string[][] { - // Base case - if (arrays.length === 1) { - const remainingValues = _.map(arrays[0], val => { - return [val]; - }); - return remainingValues; - } else { - const result = []; - const restOfArrays = arrays.slice(1); - const allCombinationsOfRemaining = FillOrderCombinatorialUtils._getAllCombinations(restOfArrays); // recur with the rest of array - // tslint:disable:prefer-for-of - for (let i = 0; i < allCombinationsOfRemaining.length; i++) { - for (let j = 0; j < arrays[0].length; j++) { - result.push([arrays[0][j], ...allCombinationsOfRemaining[i]]); - } - } - // tslint:enable:prefer-for-of - return result; - } - } - constructor( - orderFactory: OrderFactoryFromScenario, - ownerAddress: string, - makerAddress: string, - makerPrivateKey: Buffer, - takerAddress: string, - zrxAssetData: string, - exchangeWrapper: ExchangeWrapper, - assetWrapper: AssetWrapper, - testLibsContract: TestLibsContract, - ) { - this.orderFactory = orderFactory; - this.ownerAddress = ownerAddress; - this.makerAddress = makerAddress; - this.makerPrivateKey = makerPrivateKey; - this.takerAddress = takerAddress; - this.zrxAssetData = zrxAssetData; - this.exchangeWrapper = exchangeWrapper; - this.assetWrapper = assetWrapper; - this.testLibsContract = testLibsContract; - } - public async testFillOrderScenarioAsync( - provider: Provider, - fillScenario: FillScenario, - isVerbose: boolean = false, - ): Promise<void> { - // 1. Generate order - const order = this.orderFactory.generateOrder(fillScenario.orderScenario); - - // 2. Sign order - const orderHashBuff = orderHashUtils.getOrderHashBuffer(order); - const signature = signingUtils.signMessage(orderHashBuff, this.makerPrivateKey, SignatureType.EthSign); - const signedOrder = { - ...order, - signature: `0x${signature.toString('hex')}`, - }; - - const balanceAndProxyAllowanceFetcher = new SimpleAssetBalanceAndProxyAllowanceFetcher(this.assetWrapper); - const orderFilledCancelledFetcher = new SimpleOrderFilledCancelledFetcher( - this.exchangeWrapper, - this.zrxAssetData, - ); - - // 3. Figure out fill amount - const takerAssetFillAmount = await this._getTakerAssetFillAmountAsync( - signedOrder, - fillScenario.takerAssetFillAmountScenario, - balanceAndProxyAllowanceFetcher, - orderFilledCancelledFetcher, - ); - - // 4. Permutate the maker and taker balance/allowance scenarios - await this._modifyTraderStateAsync( - fillScenario.makerStateScenario, - fillScenario.takerStateScenario, - signedOrder, - takerAssetFillAmount, - ); - - // 5. If I fill it by X, what are the resulting balances/allowances/filled amounts expected? - const orderValidationUtils = new OrderValidationUtils(orderFilledCancelledFetcher, provider); - const lazyStore = new BalanceAndProxyAllowanceLazyStore(balanceAndProxyAllowanceFetcher); - const exchangeTransferSimulator = new ExchangeTransferSimulator(lazyStore); - - let fillRevertReasonIfExists; - try { - await orderValidationUtils.validateFillOrderThrowIfInvalidAsync( - exchangeTransferSimulator, - provider, - signedOrder, - takerAssetFillAmount, - this.takerAddress, - this.zrxAssetData, - ); - if (isVerbose) { - logUtils.log(`Expecting fillOrder to succeed.`); - } - } catch (err) { - fillRevertReasonIfExists = err.message; - if (isVerbose) { - logUtils.log(`Expecting fillOrder to fail with:`); - logUtils.log(err); - } - } - - // 6. Fill the order - await this._fillOrderAndAssertOutcomeAsync( - signedOrder, - takerAssetFillAmount, - lazyStore, - fillRevertReasonIfExists, - ); - - await this._abiEncodeFillOrderAndAssertOutcomeAsync(signedOrder, takerAssetFillAmount); - } - private async _fillOrderAndAssertOutcomeAsync( - signedOrder: SignedOrder, - takerAssetFillAmount: BigNumber, - lazyStore: BalanceAndProxyAllowanceLazyStore, - fillRevertReasonIfExists: RevertReason | undefined, - ): Promise<void> { - if (!_.isUndefined(fillRevertReasonIfExists)) { - return expectTransactionFailedAsync( - this.exchangeWrapper.fillOrderAsync(signedOrder, this.takerAddress, { takerAssetFillAmount }), - fillRevertReasonIfExists, - ); - } - - const makerAddress = signedOrder.makerAddress; - const makerAssetData = signedOrder.makerAssetData; - const takerAssetData = signedOrder.takerAssetData; - const feeRecipient = signedOrder.feeRecipientAddress; - - const expMakerAssetBalanceOfMaker = await lazyStore.getBalanceAsync(makerAssetData, makerAddress); - const expMakerAssetAllowanceOfMaker = await lazyStore.getProxyAllowanceAsync(makerAssetData, makerAddress); - const expTakerAssetBalanceOfMaker = await lazyStore.getBalanceAsync(takerAssetData, makerAddress); - const expZRXAssetBalanceOfMaker = await lazyStore.getBalanceAsync(this.zrxAssetData, makerAddress); - const expZRXAssetAllowanceOfMaker = await lazyStore.getProxyAllowanceAsync(this.zrxAssetData, makerAddress); - const expTakerAssetBalanceOfTaker = await lazyStore.getBalanceAsync(takerAssetData, this.takerAddress); - const expTakerAssetAllowanceOfTaker = await lazyStore.getProxyAllowanceAsync(takerAssetData, this.takerAddress); - const expMakerAssetBalanceOfTaker = await lazyStore.getBalanceAsync(makerAssetData, this.takerAddress); - const expZRXAssetBalanceOfTaker = await lazyStore.getBalanceAsync(this.zrxAssetData, this.takerAddress); - const expZRXAssetAllowanceOfTaker = await lazyStore.getProxyAllowanceAsync( - this.zrxAssetData, - this.takerAddress, - ); - const expZRXAssetBalanceOfFeeRecipient = await lazyStore.getBalanceAsync(this.zrxAssetData, feeRecipient); - - const orderHash = orderHashUtils.getOrderHashHex(signedOrder); - const alreadyFilledTakerAmount = await this.exchangeWrapper.getTakerAssetFilledAmountAsync(orderHash); - const remainingTakerAmountToFill = signedOrder.takerAssetAmount.minus(alreadyFilledTakerAmount); - const expFilledTakerAmount = takerAssetFillAmount.gt(remainingTakerAmountToFill) - ? remainingTakerAmountToFill - : alreadyFilledTakerAmount.add(takerAssetFillAmount); - - const expFilledMakerAmount = orderUtils.getPartialAmountFloor( - expFilledTakerAmount, - signedOrder.takerAssetAmount, - signedOrder.makerAssetAmount, - ); - const expMakerFeePaid = orderUtils.getPartialAmountFloor( - expFilledTakerAmount, - signedOrder.takerAssetAmount, - signedOrder.makerFee, - ); - const expTakerFeePaid = orderUtils.getPartialAmountFloor( - expFilledTakerAmount, - signedOrder.takerAssetAmount, - signedOrder.takerFee, - ); - const fillResults = await this.exchangeWrapper.getFillOrderResultsAsync(signedOrder, this.takerAddress, { - takerAssetFillAmount, - }); - expect(fillResults.takerAssetFilledAmount).to.be.bignumber.equal( - expFilledTakerAmount, - 'takerAssetFilledAmount', - ); - expect(fillResults.makerAssetFilledAmount).to.be.bignumber.equal( - expFilledMakerAmount, - 'makerAssetFilledAmount', - ); - expect(fillResults.takerFeePaid).to.be.bignumber.equal(expTakerFeePaid, 'takerFeePaid'); - expect(fillResults.makerFeePaid).to.be.bignumber.equal(expMakerFeePaid, 'makerFeePaid'); - - // - Let's fill the order! - const txReceipt = await this.exchangeWrapper.fillOrderAsync(signedOrder, this.takerAddress, { - takerAssetFillAmount, - }); - - const actFilledTakerAmount = await this.exchangeWrapper.getTakerAssetFilledAmountAsync(orderHash); - expect(actFilledTakerAmount).to.be.bignumber.equal(expFilledTakerAmount, 'filledTakerAmount'); - - const exchangeLogs = _.filter( - txReceipt.logs, - txLog => txLog.address === this.exchangeWrapper.getExchangeAddress(), - ); - expect(exchangeLogs.length).to.be.equal(1, 'logs length'); - // tslint:disable-next-line:no-unnecessary-type-assertion - const log = txReceipt.logs[0] as LogWithDecodedArgs<ExchangeFillEventArgs>; - expect(log.args.makerAddress).to.be.equal(makerAddress, 'log.args.makerAddress'); - expect(log.args.takerAddress).to.be.equal(this.takerAddress, 'log.args.this.takerAddress'); - expect(log.args.feeRecipientAddress).to.be.equal(feeRecipient, 'log.args.feeRecipientAddress'); - expect(log.args.makerAssetFilledAmount).to.be.bignumber.equal( - expFilledMakerAmount, - 'log.args.makerAssetFilledAmount', - ); - expect(log.args.takerAssetFilledAmount).to.be.bignumber.equal( - expFilledTakerAmount, - 'log.args.takerAssetFilledAmount', - ); - expect(log.args.makerFeePaid).to.be.bignumber.equal(expMakerFeePaid, 'log.args.makerFeePaid'); - expect(log.args.takerFeePaid).to.be.bignumber.equal(expTakerFeePaid, 'logs.args.takerFeePaid'); - expect(log.args.orderHash).to.be.equal(orderHash, 'log.args.orderHash'); - expect(log.args.makerAssetData).to.be.equal(makerAssetData, 'log.args.makerAssetData'); - expect(log.args.takerAssetData).to.be.equal(takerAssetData, 'log.args.takerAssetData'); - - const actMakerAssetBalanceOfMaker = await this.assetWrapper.getBalanceAsync(makerAddress, makerAssetData); - expect(actMakerAssetBalanceOfMaker).to.be.bignumber.equal( - expMakerAssetBalanceOfMaker, - 'makerAssetBalanceOfMaker', - ); - - const actMakerAssetAllowanceOfMaker = await this.assetWrapper.getProxyAllowanceAsync( - makerAddress, - makerAssetData, - ); - expect(actMakerAssetAllowanceOfMaker).to.be.bignumber.equal( - expMakerAssetAllowanceOfMaker, - 'makerAssetAllowanceOfMaker', - ); - - const actTakerAssetBalanceOfMaker = await this.assetWrapper.getBalanceAsync(makerAddress, takerAssetData); - expect(actTakerAssetBalanceOfMaker).to.be.bignumber.equal( - expTakerAssetBalanceOfMaker, - 'takerAssetBalanceOfMaker', - ); - - const actZRXAssetBalanceOfMaker = await this.assetWrapper.getBalanceAsync(makerAddress, this.zrxAssetData); - expect(actZRXAssetBalanceOfMaker).to.be.bignumber.equal(expZRXAssetBalanceOfMaker, 'ZRXAssetBalanceOfMaker'); - - const actZRXAssetAllowanceOfMaker = await this.assetWrapper.getProxyAllowanceAsync( - makerAddress, - this.zrxAssetData, - ); - expect(actZRXAssetAllowanceOfMaker).to.be.bignumber.equal( - expZRXAssetAllowanceOfMaker, - 'ZRXAssetAllowanceOfMaker', - ); - - const actTakerAssetBalanceOfTaker = await this.assetWrapper.getBalanceAsync(this.takerAddress, takerAssetData); - expect(actTakerAssetBalanceOfTaker).to.be.bignumber.equal( - expTakerAssetBalanceOfTaker, - 'TakerAssetBalanceOfTaker', - ); - - const actTakerAssetAllowanceOfTaker = await this.assetWrapper.getProxyAllowanceAsync( - this.takerAddress, - takerAssetData, - ); - - expect(actTakerAssetAllowanceOfTaker).to.be.bignumber.equal( - expTakerAssetAllowanceOfTaker, - 'TakerAssetAllowanceOfTaker', - ); - - const actMakerAssetBalanceOfTaker = await this.assetWrapper.getBalanceAsync(this.takerAddress, makerAssetData); - expect(actMakerAssetBalanceOfTaker).to.be.bignumber.equal( - expMakerAssetBalanceOfTaker, - 'MakerAssetBalanceOfTaker', - ); - - const actZRXAssetBalanceOfTaker = await this.assetWrapper.getBalanceAsync(this.takerAddress, this.zrxAssetData); - expect(actZRXAssetBalanceOfTaker).to.be.bignumber.equal(expZRXAssetBalanceOfTaker, 'ZRXAssetBalanceOfTaker'); - - const actZRXAssetAllowanceOfTaker = await this.assetWrapper.getProxyAllowanceAsync( - this.takerAddress, - this.zrxAssetData, - ); - expect(actZRXAssetAllowanceOfTaker).to.be.bignumber.equal( - expZRXAssetAllowanceOfTaker, - 'ZRXAssetAllowanceOfTaker', - ); - - const actZRXAssetBalanceOfFeeRecipient = await this.assetWrapper.getBalanceAsync( - feeRecipient, - this.zrxAssetData, - ); - expect(actZRXAssetBalanceOfFeeRecipient).to.be.bignumber.equal( - expZRXAssetBalanceOfFeeRecipient, - 'ZRXAssetBalanceOfFeeRecipient', - ); - } - private async _abiEncodeFillOrderAndAssertOutcomeAsync( - signedOrder: SignedOrder, - takerAssetFillAmount: BigNumber, - ): Promise<void> { - const params = orderUtils.createFill(signedOrder, takerAssetFillAmount); - const expectedAbiEncodedData = this.exchangeWrapper.abiEncodeFillOrder(signedOrder, { takerAssetFillAmount }); - const libsAbiEncodedData = await this.testLibsContract.publicAbiEncodeFillOrder.callAsync( - params.order, - params.takerAssetFillAmount, - params.signature, - ); - expect(libsAbiEncodedData).to.be.equal(expectedAbiEncodedData, 'ABIEncodedFillOrderData'); - } - private async _getTakerAssetFillAmountAsync( - signedOrder: SignedOrder, - takerAssetFillAmountScenario: TakerAssetFillAmountScenario, - balanceAndProxyAllowanceFetcher: SimpleAssetBalanceAndProxyAllowanceFetcher, - orderFilledCancelledFetcher: SimpleOrderFilledCancelledFetcher, - ): Promise<BigNumber> { - const orderStateUtils = new OrderStateUtils(balanceAndProxyAllowanceFetcher, orderFilledCancelledFetcher); - const fillableTakerAssetAmount = await orderStateUtils.getMaxFillableTakerAssetAmountAsync( - signedOrder, - this.takerAddress, - ); - - let takerAssetFillAmount; - switch (takerAssetFillAmountScenario) { - case TakerAssetFillAmountScenario.Zero: - takerAssetFillAmount = new BigNumber(0); - break; - - case TakerAssetFillAmountScenario.ExactlyRemainingFillableTakerAssetAmount: - takerAssetFillAmount = fillableTakerAssetAmount; - break; - - case TakerAssetFillAmountScenario.GreaterThanRemainingFillableTakerAssetAmount: - takerAssetFillAmount = fillableTakerAssetAmount.add(1); - break; - - case TakerAssetFillAmountScenario.LessThanRemainingFillableTakerAssetAmount: - const takerAssetProxyId = assetDataUtils.decodeAssetProxyId(signedOrder.takerAssetData); - const makerAssetProxyId = assetDataUtils.decodeAssetProxyId(signedOrder.makerAssetData); - const isEitherAssetERC721 = - takerAssetProxyId === AssetProxyId.ERC721 || makerAssetProxyId === AssetProxyId.ERC721; - if (isEitherAssetERC721) { - throw new Error( - 'Cannot test `TakerAssetFillAmountScenario.LessThanRemainingFillableTakerAssetAmount` together with ERC721 assets since orders involving ERC721 must always be filled exactly.', - ); - } - takerAssetFillAmount = fillableTakerAssetAmount.div(2).floor(); - break; - - default: - throw errorUtils.spawnSwitchErr('TakerAssetFillAmountScenario', takerAssetFillAmountScenario); - } - - return takerAssetFillAmount; - } - private async _modifyTraderStateAsync( - makerStateScenario: TraderStateScenario, - takerStateScenario: TraderStateScenario, - signedOrder: SignedOrder, - takerAssetFillAmount: BigNumber, - ): Promise<void> { - const makerAssetFillAmount = orderUtils.getPartialAmountFloor( - takerAssetFillAmount, - signedOrder.takerAssetAmount, - signedOrder.makerAssetAmount, - ); - switch (makerStateScenario.traderAssetBalance) { - case BalanceAmountScenario.Higher: - break; // Noop since this is already the default - - case BalanceAmountScenario.TooLow: - if (makerAssetFillAmount.eq(0)) { - throw new Error(`Cannot set makerAssetBalanceOfMaker TooLow if makerAssetFillAmount is 0`); - } - const tooLowBalance = makerAssetFillAmount.minus(1); - await this.assetWrapper.setBalanceAsync( - signedOrder.makerAddress, - signedOrder.makerAssetData, - tooLowBalance, - ); - break; - - case BalanceAmountScenario.Exact: - const exactBalance = makerAssetFillAmount; - await this.assetWrapper.setBalanceAsync( - signedOrder.makerAddress, - signedOrder.makerAssetData, - exactBalance, - ); - break; - - default: - throw errorUtils.spawnSwitchErr( - 'makerStateScenario.traderAssetBalance', - makerStateScenario.traderAssetBalance, - ); - } - - const makerFee = orderUtils.getPartialAmountFloor( - takerAssetFillAmount, - signedOrder.takerAssetAmount, - signedOrder.makerFee, - ); - switch (makerStateScenario.zrxFeeBalance) { - case BalanceAmountScenario.Higher: - break; // Noop since this is already the default - - case BalanceAmountScenario.TooLow: - if (makerFee.eq(0)) { - throw new Error(`Cannot set zrxAsserBalanceOfMaker TooLow if makerFee is 0`); - } - const tooLowBalance = makerFee.minus(1); - await this.assetWrapper.setBalanceAsync(signedOrder.makerAddress, this.zrxAssetData, tooLowBalance); - break; - - case BalanceAmountScenario.Exact: - const exactBalance = makerFee; - await this.assetWrapper.setBalanceAsync(signedOrder.makerAddress, this.zrxAssetData, exactBalance); - break; - - default: - throw errorUtils.spawnSwitchErr('makerStateScenario.zrxFeeBalance', makerStateScenario.zrxFeeBalance); - } - - switch (makerStateScenario.traderAssetAllowance) { - case AllowanceAmountScenario.Higher: - break; // Noop since this is already the default - - case AllowanceAmountScenario.TooLow: - const tooLowAllowance = makerAssetFillAmount.minus(1); - await this.assetWrapper.setProxyAllowanceAsync( - signedOrder.makerAddress, - signedOrder.makerAssetData, - tooLowAllowance, - ); - break; - - case AllowanceAmountScenario.Exact: - const exactAllowance = makerAssetFillAmount; - await this.assetWrapper.setProxyAllowanceAsync( - signedOrder.makerAddress, - signedOrder.makerAssetData, - exactAllowance, - ); - break; - - case AllowanceAmountScenario.Unlimited: - await this.assetWrapper.setProxyAllowanceAsync( - signedOrder.makerAddress, - signedOrder.makerAssetData, - constants.UNLIMITED_ALLOWANCE_IN_BASE_UNITS, - ); - break; - - default: - throw errorUtils.spawnSwitchErr( - 'makerStateScenario.traderAssetAllowance', - makerStateScenario.traderAssetAllowance, - ); - } - - switch (makerStateScenario.zrxFeeAllowance) { - case AllowanceAmountScenario.Higher: - break; // Noop since this is already the default - - case AllowanceAmountScenario.TooLow: - const tooLowAllowance = makerFee.minus(1); - await this.assetWrapper.setProxyAllowanceAsync( - signedOrder.makerAddress, - this.zrxAssetData, - tooLowAllowance, - ); - break; - - case AllowanceAmountScenario.Exact: - const exactAllowance = makerFee; - await this.assetWrapper.setProxyAllowanceAsync( - signedOrder.makerAddress, - this.zrxAssetData, - exactAllowance, - ); - break; - - case AllowanceAmountScenario.Unlimited: - await this.assetWrapper.setProxyAllowanceAsync( - signedOrder.makerAddress, - this.zrxAssetData, - constants.UNLIMITED_ALLOWANCE_IN_BASE_UNITS, - ); - break; - - default: - throw errorUtils.spawnSwitchErr( - 'makerStateScenario.zrxFeeAllowance', - makerStateScenario.zrxFeeAllowance, - ); - } - - switch (takerStateScenario.traderAssetBalance) { - case BalanceAmountScenario.Higher: - break; // Noop since this is already the default - - case BalanceAmountScenario.TooLow: - if (takerAssetFillAmount.eq(0)) { - throw new Error(`Cannot set takerAssetBalanceOfTaker TooLow if takerAssetFillAmount is 0`); - } - const tooLowBalance = takerAssetFillAmount.minus(1); - await this.assetWrapper.setBalanceAsync(this.takerAddress, signedOrder.takerAssetData, tooLowBalance); - break; - - case BalanceAmountScenario.Exact: - const exactBalance = takerAssetFillAmount; - await this.assetWrapper.setBalanceAsync(this.takerAddress, signedOrder.takerAssetData, exactBalance); - break; - - default: - throw errorUtils.spawnSwitchErr( - 'takerStateScenario.traderAssetBalance', - takerStateScenario.traderAssetBalance, - ); - } - - const takerFee = orderUtils.getPartialAmountFloor( - takerAssetFillAmount, - signedOrder.takerAssetAmount, - signedOrder.takerFee, - ); - switch (takerStateScenario.zrxFeeBalance) { - case BalanceAmountScenario.Higher: - break; // Noop since this is already the default - - case BalanceAmountScenario.TooLow: - if (takerFee.eq(0)) { - throw new Error(`Cannot set zrxAssetBalanceOfTaker TooLow if takerFee is 0`); - } - const tooLowBalance = takerFee.minus(1); - await this.assetWrapper.setBalanceAsync(this.takerAddress, this.zrxAssetData, tooLowBalance); - break; - - case BalanceAmountScenario.Exact: - const exactBalance = takerFee; - await this.assetWrapper.setBalanceAsync(this.takerAddress, this.zrxAssetData, exactBalance); - break; - - default: - throw errorUtils.spawnSwitchErr('takerStateScenario.zrxFeeBalance', takerStateScenario.zrxFeeBalance); - } - - switch (takerStateScenario.traderAssetAllowance) { - case AllowanceAmountScenario.Higher: - break; // Noop since this is already the default - - case AllowanceAmountScenario.TooLow: - const tooLowAllowance = takerAssetFillAmount.minus(1); - await this.assetWrapper.setProxyAllowanceAsync( - this.takerAddress, - signedOrder.takerAssetData, - tooLowAllowance, - ); - break; - - case AllowanceAmountScenario.Exact: - const exactAllowance = takerAssetFillAmount; - await this.assetWrapper.setProxyAllowanceAsync( - this.takerAddress, - signedOrder.takerAssetData, - exactAllowance, - ); - break; - - case AllowanceAmountScenario.Unlimited: - await this.assetWrapper.setProxyAllowanceAsync( - this.takerAddress, - signedOrder.takerAssetData, - constants.UNLIMITED_ALLOWANCE_IN_BASE_UNITS, - ); - break; - - default: - throw errorUtils.spawnSwitchErr( - 'takerStateScenario.traderAssetAllowance', - takerStateScenario.traderAssetAllowance, - ); - } - - switch (takerStateScenario.zrxFeeAllowance) { - case AllowanceAmountScenario.Higher: - break; // Noop since this is already the default - - case AllowanceAmountScenario.TooLow: - const tooLowAllowance = takerFee.minus(1); - await this.assetWrapper.setProxyAllowanceAsync(this.takerAddress, this.zrxAssetData, tooLowAllowance); - break; - - case AllowanceAmountScenario.Exact: - const exactAllowance = takerFee; - await this.assetWrapper.setProxyAllowanceAsync(this.takerAddress, this.zrxAssetData, exactAllowance); - break; - - case AllowanceAmountScenario.Unlimited: - await this.assetWrapper.setProxyAllowanceAsync( - this.takerAddress, - this.zrxAssetData, - constants.UNLIMITED_ALLOWANCE_IN_BASE_UNITS, - ); - break; - - default: - throw errorUtils.spawnSwitchErr( - 'takerStateScenario.zrxFeeAllowance', - takerStateScenario.zrxFeeAllowance, - ); - } - } -} // tslint:disable:max-file-line-count diff --git a/contracts/core/test/utils/index.ts b/contracts/core/test/utils/index.ts deleted file mode 100644 index 75cd88666..000000000 --- a/contracts/core/test/utils/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './exchange_wrapper'; -export * from './erc20_wrapper'; -export * from './erc721_wrapper'; diff --git a/contracts/core/test/utils/match_order_tester.ts b/contracts/core/test/utils/match_order_tester.ts deleted file mode 100644 index 8f574704e..000000000 --- a/contracts/core/test/utils/match_order_tester.ts +++ /dev/null @@ -1,562 +0,0 @@ -import { - chaiSetup, - ERC20BalancesByOwner, - ERC721TokenIdsByOwner, - OrderInfo, - OrderStatus, - TransferAmountsByMatchOrders as TransferAmounts, - TransferAmountsLoggedByMatchOrders as LoggedTransferAmounts, -} from '@0x/contracts-test-utils'; -import { assetDataUtils, orderHashUtils } from '@0x/order-utils'; -import { AssetProxyId, SignedOrder } from '@0x/types'; -import { BigNumber } from '@0x/utils'; -import * as chai from 'chai'; -import * as _ from 'lodash'; - -import { TransactionReceiptWithDecodedLogs } from '../../../../node_modules/ethereum-types'; - -import { ERC20Wrapper } from './erc20_wrapper'; -import { ERC721Wrapper } from './erc721_wrapper'; -import { ExchangeWrapper } from './exchange_wrapper'; - -chaiSetup.configure(); -const expect = chai.expect; - -export class MatchOrderTester { - private readonly _exchangeWrapper: ExchangeWrapper; - private readonly _erc20Wrapper: ERC20Wrapper; - private readonly _erc721Wrapper: ERC721Wrapper; - private readonly _feeTokenAddress: string; - /// @dev Checks values from the logs produced by Exchange.matchOrders against the expected transfer amounts. - /// Values include the amounts transferred from the left/right makers and taker, along with - /// the fees paid on each matched order. These are also the return values of MatchOrders. - /// @param signedOrderLeft First matched order. - /// @param signedOrderRight Second matched order. - /// @param transactionReceipt Transaction receipt and logs produced by Exchange.matchOrders. - /// @param takerAddress Address of taker (account that called Exchange.matchOrders) - /// @param expectedTransferAmounts Expected amounts transferred as a result of order matching. - private static async _assertLogsAsync( - signedOrderLeft: SignedOrder, - signedOrderRight: SignedOrder, - transactionReceipt: TransactionReceiptWithDecodedLogs, - takerAddress: string, - expectedTransferAmounts: TransferAmounts, - ): Promise<void> { - // Should have two fill event logs -- one for each order. - const transactionFillLogs = _.filter(transactionReceipt.logs, ['event', 'Fill']); - expect(transactionFillLogs.length, 'Checking number of logs').to.be.equal(2); - // First log is for left fill - const leftLog = (transactionFillLogs[0] as any).args as LoggedTransferAmounts; - expect(leftLog.makerAddress, 'Checking logged maker address of left order').to.be.equal( - signedOrderLeft.makerAddress, - ); - expect(leftLog.takerAddress, 'Checking logged taker address of right order').to.be.equal(takerAddress); - const amountBoughtByLeftMaker = new BigNumber(leftLog.takerAssetFilledAmount); - const amountSoldByLeftMaker = new BigNumber(leftLog.makerAssetFilledAmount); - const feePaidByLeftMaker = new BigNumber(leftLog.makerFeePaid); - const feePaidByTakerLeft = new BigNumber(leftLog.takerFeePaid); - // Second log is for right fill - const rightLog = (transactionFillLogs[1] as any).args as LoggedTransferAmounts; - expect(rightLog.makerAddress, 'Checking logged maker address of right order').to.be.equal( - signedOrderRight.makerAddress, - ); - expect(rightLog.takerAddress, 'Checking loggerd taker address of right order').to.be.equal(takerAddress); - const amountBoughtByRightMaker = new BigNumber(rightLog.takerAssetFilledAmount); - const amountSoldByRightMaker = new BigNumber(rightLog.makerAssetFilledAmount); - const feePaidByRightMaker = new BigNumber(rightLog.makerFeePaid); - const feePaidByTakerRight = new BigNumber(rightLog.takerFeePaid); - // Derive amount received by taker - const amountReceivedByTaker = amountSoldByLeftMaker.sub(amountBoughtByRightMaker); - // Assert log values - left order - expect(amountBoughtByLeftMaker, 'Checking logged amount bought by left maker').to.be.bignumber.equal( - expectedTransferAmounts.amountBoughtByLeftMaker, - ); - expect(amountSoldByLeftMaker, 'Checking logged amount sold by left maker').to.be.bignumber.equal( - expectedTransferAmounts.amountSoldByLeftMaker, - ); - expect(feePaidByLeftMaker, 'Checking logged fee paid by left maker').to.be.bignumber.equal( - expectedTransferAmounts.feePaidByLeftMaker, - ); - expect(feePaidByTakerLeft, 'Checking logged fee paid on left order by taker').to.be.bignumber.equal( - expectedTransferAmounts.feePaidByTakerLeft, - ); - // Assert log values - right order - expect(amountBoughtByRightMaker, 'Checking logged amount bought by right maker').to.be.bignumber.equal( - expectedTransferAmounts.amountBoughtByRightMaker, - ); - expect(amountSoldByRightMaker, 'Checking logged amount sold by right maker').to.be.bignumber.equal( - expectedTransferAmounts.amountSoldByRightMaker, - ); - expect(feePaidByRightMaker, 'Checking logged fee paid by right maker').to.be.bignumber.equal( - expectedTransferAmounts.feePaidByRightMaker, - ); - expect(feePaidByTakerRight, 'Checking logged fee paid on right order by taker').to.be.bignumber.equal( - expectedTransferAmounts.feePaidByTakerRight, - ); - // Assert derived amount received by taker - expect(amountReceivedByTaker, 'Checking logged amount received by taker').to.be.bignumber.equal( - expectedTransferAmounts.amountReceivedByTaker, - ); - } - /// @dev Asserts all expected ERC20 and ERC721 account holdings match the real holdings. - /// @param expectedERC20BalancesByOwner Expected ERC20 balances. - /// @param realERC20BalancesByOwner Real ERC20 balances. - /// @param expectedERC721TokenIdsByOwner Expected ERC721 token owners. - /// @param realERC721TokenIdsByOwner Real ERC20 token owners. - private static async _assertAllKnownBalancesAsync( - expectedERC20BalancesByOwner: ERC20BalancesByOwner, - realERC20BalancesByOwner: ERC20BalancesByOwner, - expectedERC721TokenIdsByOwner: ERC721TokenIdsByOwner, - realERC721TokenIdsByOwner: ERC721TokenIdsByOwner, - ): Promise<void> { - // ERC20 Balances - const areERC20BalancesEqual = _.isEqual(expectedERC20BalancesByOwner, realERC20BalancesByOwner); - expect(areERC20BalancesEqual, 'Checking all known ERC20 account balances').to.be.true(); - // ERC721 Token Ids - const sortedExpectedNewERC721TokenIdsByOwner = _.mapValues(expectedERC721TokenIdsByOwner, tokenIdsByOwner => { - _.mapValues(tokenIdsByOwner, tokenIds => { - _.sortBy(tokenIds); - }); - }); - const sortedNewERC721TokenIdsByOwner = _.mapValues(realERC721TokenIdsByOwner, tokenIdsByOwner => { - _.mapValues(tokenIdsByOwner, tokenIds => { - _.sortBy(tokenIds); - }); - }); - const areERC721TokenIdsEqual = _.isEqual( - sortedExpectedNewERC721TokenIdsByOwner, - sortedNewERC721TokenIdsByOwner, - ); - expect(areERC721TokenIdsEqual, 'Checking all known ERC721 account balances').to.be.true(); - } - /// @dev Constructs new MatchOrderTester. - /// @param exchangeWrapper Used to call to the Exchange. - /// @param erc20Wrapper Used to fetch ERC20 balances. - /// @param erc721Wrapper Used to fetch ERC721 token owners. - /// @param feeTokenAddress Address of ERC20 fee token. - constructor( - exchangeWrapper: ExchangeWrapper, - erc20Wrapper: ERC20Wrapper, - erc721Wrapper: ERC721Wrapper, - feeTokenAddress: string, - ) { - this._exchangeWrapper = exchangeWrapper; - this._erc20Wrapper = erc20Wrapper; - this._erc721Wrapper = erc721Wrapper; - this._feeTokenAddress = feeTokenAddress; - } - /// @dev Matches two complementary orders and asserts results. - /// @param signedOrderLeft First matched order. - /// @param signedOrderRight Second matched order. - /// @param takerAddress Address of taker (the address who matched the two orders) - /// @param erc20BalancesByOwner Current ERC20 balances. - /// @param erc721TokenIdsByOwner Current ERC721 token owners. - /// @param expectedTransferAmounts Expected amounts transferred as a result of order matching. - /// @param initialLeftOrderFilledAmount How much left order has been filled, prior to matching orders. - /// @param initialRightOrderFilledAmount How much the right order has been filled, prior to matching orders. - /// @return New ERC20 balances & ERC721 token owners. - public async matchOrdersAndAssertEffectsAsync( - signedOrderLeft: SignedOrder, - signedOrderRight: SignedOrder, - takerAddress: string, - erc20BalancesByOwner: ERC20BalancesByOwner, - erc721TokenIdsByOwner: ERC721TokenIdsByOwner, - expectedTransferAmounts: TransferAmounts, - initialLeftOrderFilledAmount: BigNumber = new BigNumber(0), - initialRightOrderFilledAmount: BigNumber = new BigNumber(0), - ): Promise<[ERC20BalancesByOwner, ERC721TokenIdsByOwner]> { - // Assert initial order states - await this._assertInitialOrderStatesAsync( - signedOrderLeft, - signedOrderRight, - initialLeftOrderFilledAmount, - initialRightOrderFilledAmount, - ); - // Match left & right orders - const transactionReceipt = await this._exchangeWrapper.matchOrdersAsync( - signedOrderLeft, - signedOrderRight, - takerAddress, - ); - const newERC20BalancesByOwner = await this._erc20Wrapper.getBalancesAsync(); - const newERC721TokenIdsByOwner = await this._erc721Wrapper.getBalancesAsync(); - // Assert logs - await MatchOrderTester._assertLogsAsync( - signedOrderLeft, - signedOrderRight, - transactionReceipt, - takerAddress, - expectedTransferAmounts, - ); - // Assert exchange state - await this._assertExchangeStateAsync( - signedOrderLeft, - signedOrderRight, - initialLeftOrderFilledAmount, - initialRightOrderFilledAmount, - expectedTransferAmounts, - ); - // Assert balances of makers, taker, and fee recipients - await this._assertBalancesAsync( - signedOrderLeft, - signedOrderRight, - erc20BalancesByOwner, - erc721TokenIdsByOwner, - newERC20BalancesByOwner, - newERC721TokenIdsByOwner, - expectedTransferAmounts, - takerAddress, - ); - return [newERC20BalancesByOwner, newERC721TokenIdsByOwner]; - } - /// @dev Asserts initial exchange state for the left and right orders. - /// @param signedOrderLeft First matched order. - /// @param signedOrderRight Second matched order. - /// @param expectedOrderFilledAmountLeft How much left order has been filled, prior to matching orders. - /// @param expectedOrderFilledAmountRight How much the right order has been filled, prior to matching orders. - private async _assertInitialOrderStatesAsync( - signedOrderLeft: SignedOrder, - signedOrderRight: SignedOrder, - expectedOrderFilledAmountLeft: BigNumber, - expectedOrderFilledAmountRight: BigNumber, - ): Promise<void> { - // Assert left order initial state - const orderTakerAssetFilledAmountLeft = await this._exchangeWrapper.getTakerAssetFilledAmountAsync( - orderHashUtils.getOrderHashHex(signedOrderLeft), - ); - expect(orderTakerAssetFilledAmountLeft, 'Checking inital state of left order').to.be.bignumber.equal( - expectedOrderFilledAmountLeft, - ); - // Assert right order initial state - const orderTakerAssetFilledAmountRight = await this._exchangeWrapper.getTakerAssetFilledAmountAsync( - orderHashUtils.getOrderHashHex(signedOrderRight), - ); - expect(orderTakerAssetFilledAmountRight, 'Checking inital state of right order').to.be.bignumber.equal( - expectedOrderFilledAmountRight, - ); - } - /// @dev Asserts the exchange state against the expected amounts transferred by from matching orders. - /// @param signedOrderLeft First matched order. - /// @param signedOrderRight Second matched order. - /// @param initialLeftOrderFilledAmount How much left order has been filled, prior to matching orders. - /// @param initialRightOrderFilledAmount How much the right order has been filled, prior to matching orders. - /// @return TransferAmounts A struct containing the expected transfer amounts. - private async _assertExchangeStateAsync( - signedOrderLeft: SignedOrder, - signedOrderRight: SignedOrder, - initialLeftOrderFilledAmount: BigNumber, - initialRightOrderFilledAmount: BigNumber, - expectedTransferAmounts: TransferAmounts, - ): Promise<void> { - // Assert state for left order: amount bought by left maker - let amountBoughtByLeftMaker = await this._exchangeWrapper.getTakerAssetFilledAmountAsync( - orderHashUtils.getOrderHashHex(signedOrderLeft), - ); - amountBoughtByLeftMaker = amountBoughtByLeftMaker.minus(initialLeftOrderFilledAmount); - expect(amountBoughtByLeftMaker, 'Checking exchange state for left order').to.be.bignumber.equal( - expectedTransferAmounts.amountBoughtByLeftMaker, - ); - // Assert state for right order: amount bought by right maker - let amountBoughtByRightMaker = await this._exchangeWrapper.getTakerAssetFilledAmountAsync( - orderHashUtils.getOrderHashHex(signedOrderRight), - ); - amountBoughtByRightMaker = amountBoughtByRightMaker.minus(initialRightOrderFilledAmount); - expect(amountBoughtByRightMaker, 'Checking exchange state for right order').to.be.bignumber.equal( - expectedTransferAmounts.amountBoughtByRightMaker, - ); - // Assert left order status - const maxAmountBoughtByLeftMaker = signedOrderLeft.takerAssetAmount.minus(initialLeftOrderFilledAmount); - const leftOrderInfo: OrderInfo = await this._exchangeWrapper.getOrderInfoAsync(signedOrderLeft); - const leftExpectedStatus = expectedTransferAmounts.amountBoughtByLeftMaker.equals(maxAmountBoughtByLeftMaker) - ? OrderStatus.FULLY_FILLED - : OrderStatus.FILLABLE; - expect(leftOrderInfo.orderStatus, 'Checking exchange status for left order').to.be.equal(leftExpectedStatus); - // Assert right order status - const maxAmountBoughtByRightMaker = signedOrderRight.takerAssetAmount.minus(initialRightOrderFilledAmount); - const rightOrderInfo: OrderInfo = await this._exchangeWrapper.getOrderInfoAsync(signedOrderRight); - const rightExpectedStatus = expectedTransferAmounts.amountBoughtByRightMaker.equals(maxAmountBoughtByRightMaker) - ? OrderStatus.FULLY_FILLED - : OrderStatus.FILLABLE; - expect(rightOrderInfo.orderStatus, 'Checking exchange status for right order').to.be.equal(rightExpectedStatus); - } - /// @dev Asserts account balances after matching orders. - /// @param signedOrderLeft First matched order. - /// @param signedOrderRight Second matched order. - /// @param initialERC20BalancesByOwner ERC20 balances prior to order matching. - /// @param initialERC721TokenIdsByOwner ERC721 token owners prior to order matching. - /// @param finalERC20BalancesByOwner ERC20 balances after order matching. - /// @param finalERC721TokenIdsByOwner ERC721 token owners after order matching. - /// @param expectedTransferAmounts Expected amounts transferred as a result of order matching. - /// @param takerAddress Address of taker (account that called Exchange.matchOrders). - private async _assertBalancesAsync( - signedOrderLeft: SignedOrder, - signedOrderRight: SignedOrder, - initialERC20BalancesByOwner: ERC20BalancesByOwner, - initialERC721TokenIdsByOwner: ERC721TokenIdsByOwner, - finalERC20BalancesByOwner: ERC20BalancesByOwner, - finalERC721TokenIdsByOwner: ERC721TokenIdsByOwner, - expectedTransferAmounts: TransferAmounts, - takerAddress: string, - ): Promise<void> { - let expectedERC20BalancesByOwner: ERC20BalancesByOwner; - let expectedERC721TokenIdsByOwner: ERC721TokenIdsByOwner; - [expectedERC20BalancesByOwner, expectedERC721TokenIdsByOwner] = this._calculateExpectedBalances( - signedOrderLeft, - signedOrderRight, - takerAddress, - initialERC20BalancesByOwner, - initialERC721TokenIdsByOwner, - expectedTransferAmounts, - ); - // Assert balances of makers, taker, and fee recipients - await this._assertMakerTakerAndFeeRecipientBalancesAsync( - signedOrderLeft, - signedOrderRight, - expectedERC20BalancesByOwner, - finalERC20BalancesByOwner, - expectedERC721TokenIdsByOwner, - finalERC721TokenIdsByOwner, - takerAddress, - ); - // Assert balances for all known accounts - await MatchOrderTester._assertAllKnownBalancesAsync( - expectedERC20BalancesByOwner, - finalERC20BalancesByOwner, - expectedERC721TokenIdsByOwner, - finalERC721TokenIdsByOwner, - ); - } - /// @dev Calculates the expected balances of order makers, fee recipients, and the taker, - /// as a result of matching two orders. - /// @param signedOrderRight First matched order. - /// @param signedOrderRight Second matched order. - /// @param takerAddress Address of taker (the address who matched the two orders) - /// @param erc20BalancesByOwner Current ERC20 balances. - /// @param erc721TokenIdsByOwner Current ERC721 token owners. - /// @param expectedTransferAmounts Expected amounts transferred as a result of order matching. - /// @return Expected ERC20 balances & ERC721 token owners after orders have been matched. - private _calculateExpectedBalances( - signedOrderLeft: SignedOrder, - signedOrderRight: SignedOrder, - takerAddress: string, - erc20BalancesByOwner: ERC20BalancesByOwner, - erc721TokenIdsByOwner: ERC721TokenIdsByOwner, - expectedTransferAmounts: TransferAmounts, - ): [ERC20BalancesByOwner, ERC721TokenIdsByOwner] { - const makerAddressLeft = signedOrderLeft.makerAddress; - const makerAddressRight = signedOrderRight.makerAddress; - const feeRecipientAddressLeft = signedOrderLeft.feeRecipientAddress; - const feeRecipientAddressRight = signedOrderRight.feeRecipientAddress; - // Operations are performed on copies of the balances - const expectedNewERC20BalancesByOwner = _.cloneDeep(erc20BalancesByOwner); - const expectedNewERC721TokenIdsByOwner = _.cloneDeep(erc721TokenIdsByOwner); - // Left Maker Asset (Right Taker Asset) - const makerAssetProxyIdLeft = assetDataUtils.decodeAssetProxyId(signedOrderLeft.makerAssetData); - if (makerAssetProxyIdLeft === AssetProxyId.ERC20) { - // Decode asset data - const erc20AssetData = assetDataUtils.decodeERC20AssetData(signedOrderLeft.makerAssetData); - const makerAssetAddressLeft = erc20AssetData.tokenAddress; - const takerAssetAddressRight = makerAssetAddressLeft; - // Left Maker - expectedNewERC20BalancesByOwner[makerAddressLeft][makerAssetAddressLeft] = expectedNewERC20BalancesByOwner[ - makerAddressLeft - ][makerAssetAddressLeft].minus(expectedTransferAmounts.amountSoldByLeftMaker); - // Right Maker - expectedNewERC20BalancesByOwner[makerAddressRight][ - takerAssetAddressRight - ] = expectedNewERC20BalancesByOwner[makerAddressRight][takerAssetAddressRight].add( - expectedTransferAmounts.amountBoughtByRightMaker, - ); - // Taker - expectedNewERC20BalancesByOwner[takerAddress][makerAssetAddressLeft] = expectedNewERC20BalancesByOwner[ - takerAddress - ][makerAssetAddressLeft].add(expectedTransferAmounts.amountReceivedByTaker); - } else if (makerAssetProxyIdLeft === AssetProxyId.ERC721) { - // Decode asset data - const erc721AssetData = assetDataUtils.decodeERC721AssetData(signedOrderLeft.makerAssetData); - const makerAssetAddressLeft = erc721AssetData.tokenAddress; - const makerAssetIdLeft = erc721AssetData.tokenId; - const takerAssetAddressRight = makerAssetAddressLeft; - const takerAssetIdRight = makerAssetIdLeft; - // Left Maker - _.remove(expectedNewERC721TokenIdsByOwner[makerAddressLeft][makerAssetAddressLeft], makerAssetIdLeft); - // Right Maker - expectedNewERC721TokenIdsByOwner[makerAddressRight][takerAssetAddressRight].push(takerAssetIdRight); - // Taker: Since there is only 1 asset transferred, the taker does not receive any of the left maker asset. - } - // Left Taker Asset (Right Maker Asset) - // Note: This exchange is only between the order makers: the Taker does not receive any of the left taker asset. - const takerAssetProxyIdLeft = assetDataUtils.decodeAssetProxyId(signedOrderLeft.takerAssetData); - if (takerAssetProxyIdLeft === AssetProxyId.ERC20) { - // Decode asset data - const erc20AssetData = assetDataUtils.decodeERC20AssetData(signedOrderLeft.takerAssetData); - const takerAssetAddressLeft = erc20AssetData.tokenAddress; - const makerAssetAddressRight = takerAssetAddressLeft; - // Left Maker - expectedNewERC20BalancesByOwner[makerAddressLeft][takerAssetAddressLeft] = expectedNewERC20BalancesByOwner[ - makerAddressLeft - ][takerAssetAddressLeft].add(expectedTransferAmounts.amountBoughtByLeftMaker); - // Right Maker - expectedNewERC20BalancesByOwner[makerAddressRight][ - makerAssetAddressRight - ] = expectedNewERC20BalancesByOwner[makerAddressRight][makerAssetAddressRight].minus( - expectedTransferAmounts.amountSoldByRightMaker, - ); - } else if (takerAssetProxyIdLeft === AssetProxyId.ERC721) { - // Decode asset data - const erc721AssetData = assetDataUtils.decodeERC721AssetData(signedOrderRight.makerAssetData); - const makerAssetAddressRight = erc721AssetData.tokenAddress; - const makerAssetIdRight = erc721AssetData.tokenId; - const takerAssetAddressLeft = makerAssetAddressRight; - const takerAssetIdLeft = makerAssetIdRight; - // Right Maker - _.remove(expectedNewERC721TokenIdsByOwner[makerAddressRight][makerAssetAddressRight], makerAssetIdRight); - // Left Maker - expectedNewERC721TokenIdsByOwner[makerAddressLeft][takerAssetAddressLeft].push(takerAssetIdLeft); - } - // Left Maker Fees - expectedNewERC20BalancesByOwner[makerAddressLeft][this._feeTokenAddress] = expectedNewERC20BalancesByOwner[ - makerAddressLeft - ][this._feeTokenAddress].minus(expectedTransferAmounts.feePaidByLeftMaker); - // Right Maker Fees - expectedNewERC20BalancesByOwner[makerAddressRight][this._feeTokenAddress] = expectedNewERC20BalancesByOwner[ - makerAddressRight - ][this._feeTokenAddress].minus(expectedTransferAmounts.feePaidByRightMaker); - // Taker Fees - expectedNewERC20BalancesByOwner[takerAddress][this._feeTokenAddress] = expectedNewERC20BalancesByOwner[ - takerAddress - ][this._feeTokenAddress].minus( - expectedTransferAmounts.feePaidByTakerLeft.add(expectedTransferAmounts.feePaidByTakerRight), - ); - // Left Fee Recipient Fees - expectedNewERC20BalancesByOwner[feeRecipientAddressLeft][ - this._feeTokenAddress - ] = expectedNewERC20BalancesByOwner[feeRecipientAddressLeft][this._feeTokenAddress].add( - expectedTransferAmounts.feePaidByLeftMaker.add(expectedTransferAmounts.feePaidByTakerLeft), - ); - // Right Fee Recipient Fees - expectedNewERC20BalancesByOwner[feeRecipientAddressRight][ - this._feeTokenAddress - ] = expectedNewERC20BalancesByOwner[feeRecipientAddressRight][this._feeTokenAddress].add( - expectedTransferAmounts.feePaidByRightMaker.add(expectedTransferAmounts.feePaidByTakerRight), - ); - - return [expectedNewERC20BalancesByOwner, expectedNewERC721TokenIdsByOwner]; - } - /// @dev Asserts ERC20 account balances and ERC721 token holdings that result from order matching. - /// Specifically checks balances of makers, taker and fee recipients. - /// @param signedOrderLeft First matched order. - /// @param signedOrderRight Second matched order. - /// @param expectedERC20BalancesByOwner Expected ERC20 balances. - /// @param realERC20BalancesByOwner Real ERC20 balances. - /// @param expectedERC721TokenIdsByOwner Expected ERC721 token owners. - /// @param realERC721TokenIdsByOwner Real ERC20 token owners. - /// @param takerAddress Address of taker (account that called Exchange.matchOrders). - private async _assertMakerTakerAndFeeRecipientBalancesAsync( - signedOrderLeft: SignedOrder, - signedOrderRight: SignedOrder, - expectedERC20BalancesByOwner: ERC20BalancesByOwner, - realERC20BalancesByOwner: ERC20BalancesByOwner, - expectedERC721TokenIdsByOwner: ERC721TokenIdsByOwner, - realERC721TokenIdsByOwner: ERC721TokenIdsByOwner, - takerAddress: string, - ): Promise<void> { - // Individual balance comparisons - const makerAssetProxyIdLeft = assetDataUtils.decodeAssetProxyId(signedOrderLeft.makerAssetData); - const makerERC20AssetDataLeft = - makerAssetProxyIdLeft === AssetProxyId.ERC20 - ? assetDataUtils.decodeERC20AssetData(signedOrderLeft.makerAssetData) - : assetDataUtils.decodeERC721AssetData(signedOrderLeft.makerAssetData); - const makerAssetAddressLeft = makerERC20AssetDataLeft.tokenAddress; - const makerAssetProxyIdRight = assetDataUtils.decodeAssetProxyId(signedOrderRight.makerAssetData); - const makerERC20AssetDataRight = - makerAssetProxyIdRight === AssetProxyId.ERC20 - ? assetDataUtils.decodeERC20AssetData(signedOrderRight.makerAssetData) - : assetDataUtils.decodeERC721AssetData(signedOrderRight.makerAssetData); - const makerAssetAddressRight = makerERC20AssetDataRight.tokenAddress; - if (makerAssetProxyIdLeft === AssetProxyId.ERC20) { - expect( - realERC20BalancesByOwner[signedOrderLeft.makerAddress][makerAssetAddressLeft], - 'Checking left maker egress ERC20 account balance', - ).to.be.bignumber.equal(expectedERC20BalancesByOwner[signedOrderLeft.makerAddress][makerAssetAddressLeft]); - expect( - realERC20BalancesByOwner[signedOrderRight.makerAddress][makerAssetAddressLeft], - 'Checking right maker ingress ERC20 account balance', - ).to.be.bignumber.equal(expectedERC20BalancesByOwner[signedOrderRight.makerAddress][makerAssetAddressLeft]); - expect( - realERC20BalancesByOwner[takerAddress][makerAssetAddressLeft], - 'Checking taker ingress ERC20 account balance', - ).to.be.bignumber.equal(expectedERC20BalancesByOwner[takerAddress][makerAssetAddressLeft]); - } else if (makerAssetProxyIdLeft === AssetProxyId.ERC721) { - expect( - realERC721TokenIdsByOwner[signedOrderLeft.makerAddress][makerAssetAddressLeft].sort(), - 'Checking left maker egress ERC721 account holdings', - ).to.be.deep.equal( - expectedERC721TokenIdsByOwner[signedOrderLeft.makerAddress][makerAssetAddressLeft].sort(), - ); - expect( - realERC721TokenIdsByOwner[signedOrderRight.makerAddress][makerAssetAddressLeft].sort(), - 'Checking right maker ERC721 account holdings', - ).to.be.deep.equal( - expectedERC721TokenIdsByOwner[signedOrderRight.makerAddress][makerAssetAddressLeft].sort(), - ); - expect( - realERC721TokenIdsByOwner[takerAddress][makerAssetAddressLeft].sort(), - 'Checking taker ingress ERC721 account holdings', - ).to.be.deep.equal(expectedERC721TokenIdsByOwner[takerAddress][makerAssetAddressLeft].sort()); - } else { - throw new Error(`Unhandled Asset Proxy ID: ${makerAssetProxyIdLeft}`); - } - if (makerAssetProxyIdRight === AssetProxyId.ERC20) { - expect( - realERC20BalancesByOwner[signedOrderLeft.makerAddress][makerAssetAddressRight], - 'Checking left maker ingress ERC20 account balance', - ).to.be.bignumber.equal(expectedERC20BalancesByOwner[signedOrderLeft.makerAddress][makerAssetAddressRight]); - expect( - realERC20BalancesByOwner[signedOrderRight.makerAddress][makerAssetAddressRight], - 'Checking right maker egress ERC20 account balance', - ).to.be.bignumber.equal( - expectedERC20BalancesByOwner[signedOrderRight.makerAddress][makerAssetAddressRight], - ); - } else if (makerAssetProxyIdRight === AssetProxyId.ERC721) { - expect( - realERC721TokenIdsByOwner[signedOrderLeft.makerAddress][makerAssetAddressRight].sort(), - 'Checking left maker ingress ERC721 account holdings', - ).to.be.deep.equal( - expectedERC721TokenIdsByOwner[signedOrderLeft.makerAddress][makerAssetAddressRight].sort(), - ); - expect( - realERC721TokenIdsByOwner[signedOrderRight.makerAddress][makerAssetAddressRight], - 'Checking right maker agress ERC721 account holdings', - ).to.be.deep.equal(expectedERC721TokenIdsByOwner[signedOrderRight.makerAddress][makerAssetAddressRight]); - } else { - throw new Error(`Unhandled Asset Proxy ID: ${makerAssetProxyIdRight}`); - } - // Paid fees - expect( - realERC20BalancesByOwner[signedOrderLeft.makerAddress][this._feeTokenAddress], - 'Checking left maker egress ERC20 account fees', - ).to.be.bignumber.equal(expectedERC20BalancesByOwner[signedOrderLeft.makerAddress][this._feeTokenAddress]); - expect( - realERC20BalancesByOwner[signedOrderRight.makerAddress][this._feeTokenAddress], - 'Checking right maker egress ERC20 account fees', - ).to.be.bignumber.equal(expectedERC20BalancesByOwner[signedOrderRight.makerAddress][this._feeTokenAddress]); - expect( - realERC20BalancesByOwner[takerAddress][this._feeTokenAddress], - 'Checking taker egress ERC20 account fees', - ).to.be.bignumber.equal(expectedERC20BalancesByOwner[takerAddress][this._feeTokenAddress]); - // Received fees - expect( - realERC20BalancesByOwner[signedOrderLeft.feeRecipientAddress][this._feeTokenAddress], - 'Checking left fee recipient ingress ERC20 account fees', - ).to.be.bignumber.equal( - expectedERC20BalancesByOwner[signedOrderLeft.feeRecipientAddress][this._feeTokenAddress], - ); - expect( - realERC20BalancesByOwner[signedOrderRight.feeRecipientAddress][this._feeTokenAddress], - 'Checking right fee receipient ingress ERC20 account fees', - ).to.be.bignumber.equal( - expectedERC20BalancesByOwner[signedOrderRight.feeRecipientAddress][this._feeTokenAddress], - ); - } -} // tslint:disable-line:max-file-line-count diff --git a/contracts/core/test/utils/order_factory_from_scenario.ts b/contracts/core/test/utils/order_factory_from_scenario.ts deleted file mode 100644 index ecb4b2e28..000000000 --- a/contracts/core/test/utils/order_factory_from_scenario.ts +++ /dev/null @@ -1,294 +0,0 @@ -import { - AssetDataScenario, - constants, - ERC721TokenIdsByOwner, - ExpirationTimeSecondsScenario, - FeeRecipientAddressScenario, - OrderAssetAmountScenario, - OrderScenario, - TakerScenario, -} from '@0x/contracts-test-utils'; -import { DummyERC721TokenContract } from '@0x/contracts-tokens'; -import { assetDataUtils, generatePseudoRandomSalt } from '@0x/order-utils'; -import { Order } from '@0x/types'; -import { BigNumber, errorUtils } from '@0x/utils'; - -const TEN_UNITS_EIGHTEEN_DECIMALS = new BigNumber(10_000_000_000_000_000_000); -const FIVE_UNITS_EIGHTEEN_DECIMALS = new BigNumber(5_000_000_000_000_000_000); -const POINT_ONE_UNITS_EIGHTEEN_DECIMALS = new BigNumber(100_000_000_000_000_000); -const POINT_ZERO_FIVE_UNITS_EIGHTEEN_DECIMALS = new BigNumber(50_000_000_000_000_000); -const TEN_UNITS_FIVE_DECIMALS = new BigNumber(1_000_000); -const FIVE_UNITS_FIVE_DECIMALS = new BigNumber(500_000); -const TEN_UNITS_ZERO_DECIMALS = new BigNumber(10); -const ONE_THOUSAND_UNITS_ZERO_DECIMALS = new BigNumber(1000); -const ONE_NFT_UNIT = new BigNumber(1); - -export class OrderFactoryFromScenario { - private readonly _userAddresses: string[]; - private readonly _zrxAddress: string; - private readonly _nonZrxERC20EighteenDecimalTokenAddresses: string[]; - private readonly _erc20FiveDecimalTokenAddresses: string[]; - private readonly _erc20ZeroDecimalTokenAddresses: string[]; - private readonly _erc721Token: DummyERC721TokenContract; - private readonly _erc721Balances: ERC721TokenIdsByOwner; - private readonly _exchangeAddress: string; - constructor( - userAddresses: string[], - zrxAddress: string, - nonZrxERC20EighteenDecimalTokenAddresses: string[], - erc20FiveDecimalTokenAddresses: string[], - erc20ZeroDecimalTokenAddresses: string[], - erc721Token: DummyERC721TokenContract, - erc721Balances: ERC721TokenIdsByOwner, - exchangeAddress: string, - ) { - this._userAddresses = userAddresses; - this._zrxAddress = zrxAddress; - this._nonZrxERC20EighteenDecimalTokenAddresses = nonZrxERC20EighteenDecimalTokenAddresses; - this._erc20FiveDecimalTokenAddresses = erc20FiveDecimalTokenAddresses; - this._erc20ZeroDecimalTokenAddresses = erc20ZeroDecimalTokenAddresses; - this._erc721Token = erc721Token; - this._erc721Balances = erc721Balances; - this._exchangeAddress = exchangeAddress; - } - public generateOrder(orderScenario: OrderScenario): Order { - const makerAddress = this._userAddresses[1]; - let takerAddress = this._userAddresses[2]; - const erc721MakerAssetIds = this._erc721Balances[makerAddress][this._erc721Token.address]; - const erc721TakerAssetIds = this._erc721Balances[takerAddress][this._erc721Token.address]; - let feeRecipientAddress; - let makerAssetAmount; - let takerAssetAmount; - let makerFee; - let takerFee; - let expirationTimeSeconds; - let makerAssetData; - let takerAssetData; - - switch (orderScenario.feeRecipientScenario) { - case FeeRecipientAddressScenario.BurnAddress: - feeRecipientAddress = constants.NULL_ADDRESS; - break; - case FeeRecipientAddressScenario.EthUserAddress: - feeRecipientAddress = this._userAddresses[4]; - break; - default: - throw errorUtils.spawnSwitchErr('FeeRecipientAddressScenario', orderScenario.feeRecipientScenario); - } - - switch (orderScenario.makerAssetDataScenario) { - case AssetDataScenario.ZRXFeeToken: - makerAssetData = assetDataUtils.encodeERC20AssetData(this._zrxAddress); - break; - case AssetDataScenario.ERC20NonZRXEighteenDecimals: - makerAssetData = assetDataUtils.encodeERC20AssetData(this._nonZrxERC20EighteenDecimalTokenAddresses[0]); - break; - case AssetDataScenario.ERC20FiveDecimals: - makerAssetData = assetDataUtils.encodeERC20AssetData(this._erc20FiveDecimalTokenAddresses[0]); - break; - case AssetDataScenario.ERC721: - makerAssetData = assetDataUtils.encodeERC721AssetData( - this._erc721Token.address, - erc721MakerAssetIds[0], - ); - break; - case AssetDataScenario.ERC20ZeroDecimals: - makerAssetData = assetDataUtils.encodeERC20AssetData(this._erc20ZeroDecimalTokenAddresses[0]); - break; - default: - throw errorUtils.spawnSwitchErr('AssetDataScenario', orderScenario.makerAssetDataScenario); - } - - switch (orderScenario.takerAssetDataScenario) { - case AssetDataScenario.ZRXFeeToken: - takerAssetData = assetDataUtils.encodeERC20AssetData(this._zrxAddress); - break; - case AssetDataScenario.ERC20NonZRXEighteenDecimals: - takerAssetData = assetDataUtils.encodeERC20AssetData(this._nonZrxERC20EighteenDecimalTokenAddresses[1]); - break; - case AssetDataScenario.ERC20FiveDecimals: - takerAssetData = assetDataUtils.encodeERC20AssetData(this._erc20FiveDecimalTokenAddresses[1]); - break; - case AssetDataScenario.ERC721: - takerAssetData = assetDataUtils.encodeERC721AssetData( - this._erc721Token.address, - erc721TakerAssetIds[0], - ); - break; - case AssetDataScenario.ERC20ZeroDecimals: - takerAssetData = assetDataUtils.encodeERC20AssetData(this._erc20ZeroDecimalTokenAddresses[1]); - break; - default: - throw errorUtils.spawnSwitchErr('AssetDataScenario', orderScenario.takerAssetDataScenario); - } - - switch (orderScenario.makerAssetAmountScenario) { - case OrderAssetAmountScenario.Large: - switch (orderScenario.makerAssetDataScenario) { - case AssetDataScenario.ZRXFeeToken: - case AssetDataScenario.ERC20NonZRXEighteenDecimals: - makerAssetAmount = TEN_UNITS_EIGHTEEN_DECIMALS; - break; - case AssetDataScenario.ERC20FiveDecimals: - makerAssetAmount = TEN_UNITS_FIVE_DECIMALS; - break; - case AssetDataScenario.ERC721: - makerAssetAmount = ONE_NFT_UNIT; - break; - case AssetDataScenario.ERC20ZeroDecimals: - makerAssetAmount = ONE_THOUSAND_UNITS_ZERO_DECIMALS; - break; - default: - throw errorUtils.spawnSwitchErr('AssetDataScenario', orderScenario.makerAssetDataScenario); - } - break; - case OrderAssetAmountScenario.Small: - switch (orderScenario.makerAssetDataScenario) { - case AssetDataScenario.ZRXFeeToken: - case AssetDataScenario.ERC20NonZRXEighteenDecimals: - makerAssetAmount = FIVE_UNITS_EIGHTEEN_DECIMALS; - break; - case AssetDataScenario.ERC20FiveDecimals: - makerAssetAmount = FIVE_UNITS_FIVE_DECIMALS; - break; - case AssetDataScenario.ERC721: - makerAssetAmount = ONE_NFT_UNIT; - break; - case AssetDataScenario.ERC20ZeroDecimals: - makerAssetAmount = TEN_UNITS_ZERO_DECIMALS; - break; - default: - throw errorUtils.spawnSwitchErr('AssetDataScenario', orderScenario.makerAssetDataScenario); - } - break; - case OrderAssetAmountScenario.Zero: - makerAssetAmount = new BigNumber(0); - break; - default: - throw errorUtils.spawnSwitchErr('OrderAssetAmountScenario', orderScenario.makerAssetAmountScenario); - } - - switch (orderScenario.takerAssetAmountScenario) { - case OrderAssetAmountScenario.Large: - switch (orderScenario.takerAssetDataScenario) { - case AssetDataScenario.ERC20NonZRXEighteenDecimals: - case AssetDataScenario.ZRXFeeToken: - takerAssetAmount = TEN_UNITS_EIGHTEEN_DECIMALS; - break; - case AssetDataScenario.ERC20FiveDecimals: - takerAssetAmount = TEN_UNITS_FIVE_DECIMALS; - break; - case AssetDataScenario.ERC721: - takerAssetAmount = ONE_NFT_UNIT; - break; - case AssetDataScenario.ERC20ZeroDecimals: - takerAssetAmount = ONE_THOUSAND_UNITS_ZERO_DECIMALS; - break; - default: - throw errorUtils.spawnSwitchErr('AssetDataScenario', orderScenario.takerAssetDataScenario); - } - break; - case OrderAssetAmountScenario.Small: - switch (orderScenario.takerAssetDataScenario) { - case AssetDataScenario.ERC20NonZRXEighteenDecimals: - case AssetDataScenario.ZRXFeeToken: - takerAssetAmount = FIVE_UNITS_EIGHTEEN_DECIMALS; - break; - case AssetDataScenario.ERC20FiveDecimals: - takerAssetAmount = FIVE_UNITS_FIVE_DECIMALS; - break; - case AssetDataScenario.ERC721: - takerAssetAmount = ONE_NFT_UNIT; - break; - case AssetDataScenario.ERC20ZeroDecimals: - takerAssetAmount = TEN_UNITS_ZERO_DECIMALS; - break; - default: - throw errorUtils.spawnSwitchErr('AssetDataScenario', orderScenario.takerAssetDataScenario); - } - break; - case OrderAssetAmountScenario.Zero: - takerAssetAmount = new BigNumber(0); - break; - default: - throw errorUtils.spawnSwitchErr('OrderAssetAmountScenario', orderScenario.takerAssetAmountScenario); - } - - switch (orderScenario.makerFeeScenario) { - case OrderAssetAmountScenario.Large: - makerFee = POINT_ONE_UNITS_EIGHTEEN_DECIMALS; - break; - case OrderAssetAmountScenario.Small: - makerFee = POINT_ZERO_FIVE_UNITS_EIGHTEEN_DECIMALS; - break; - case OrderAssetAmountScenario.Zero: - makerFee = new BigNumber(0); - break; - default: - throw errorUtils.spawnSwitchErr('OrderAssetAmountScenario', orderScenario.makerFeeScenario); - } - - switch (orderScenario.takerFeeScenario) { - case OrderAssetAmountScenario.Large: - takerFee = POINT_ONE_UNITS_EIGHTEEN_DECIMALS; - break; - case OrderAssetAmountScenario.Small: - takerFee = POINT_ZERO_FIVE_UNITS_EIGHTEEN_DECIMALS; - break; - case OrderAssetAmountScenario.Zero: - takerFee = new BigNumber(0); - break; - default: - throw errorUtils.spawnSwitchErr('OrderAssetAmountScenario', orderScenario.takerFeeScenario); - } - - switch (orderScenario.expirationTimeSecondsScenario) { - case ExpirationTimeSecondsScenario.InFuture: - expirationTimeSeconds = new BigNumber(2524604400); // Close to infinite - break; - case ExpirationTimeSecondsScenario.InPast: - expirationTimeSeconds = new BigNumber(0); // Jan 1, 1970 - break; - default: - throw errorUtils.spawnSwitchErr( - 'ExpirationTimeSecondsScenario', - orderScenario.expirationTimeSecondsScenario, - ); - } - - switch (orderScenario.takerScenario) { - case TakerScenario.CorrectlySpecified: - break; // noop since takerAddress is already specified - - case TakerScenario.IncorrectlySpecified: - const notTaker = this._userAddresses[3]; - takerAddress = notTaker; - break; - - case TakerScenario.Unspecified: - takerAddress = constants.NULL_ADDRESS; - break; - - default: - throw errorUtils.spawnSwitchErr('TakerScenario', orderScenario.takerScenario); - } - - const order = { - senderAddress: constants.NULL_ADDRESS, - makerAddress, - takerAddress, - makerFee, - takerFee, - makerAssetAmount, - takerAssetAmount, - makerAssetData, - takerAssetData, - salt: generatePseudoRandomSalt(), - exchangeAddress: this._exchangeAddress, - feeRecipientAddress, - expirationTimeSeconds, - }; - - return order; - } -} diff --git a/contracts/core/test/utils/simple_asset_balance_and_proxy_allowance_fetcher.ts b/contracts/core/test/utils/simple_asset_balance_and_proxy_allowance_fetcher.ts deleted file mode 100644 index 64b7dedbe..000000000 --- a/contracts/core/test/utils/simple_asset_balance_and_proxy_allowance_fetcher.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { AbstractBalanceAndProxyAllowanceFetcher } from '@0x/order-utils'; -import { BigNumber } from '@0x/utils'; - -import { AssetWrapper } from './asset_wrapper'; - -export class SimpleAssetBalanceAndProxyAllowanceFetcher implements AbstractBalanceAndProxyAllowanceFetcher { - private readonly _assetWrapper: AssetWrapper; - constructor(assetWrapper: AssetWrapper) { - this._assetWrapper = assetWrapper; - } - public async getBalanceAsync(assetData: string, userAddress: string): Promise<BigNumber> { - const balance = await this._assetWrapper.getBalanceAsync(userAddress, assetData); - return balance; - } - public async getProxyAllowanceAsync(assetData: string, userAddress: string): Promise<BigNumber> { - const proxyAllowance = await this._assetWrapper.getProxyAllowanceAsync(userAddress, assetData); - return proxyAllowance; - } -} diff --git a/contracts/core/test/utils/simple_order_filled_cancelled_fetcher.ts b/contracts/core/test/utils/simple_order_filled_cancelled_fetcher.ts deleted file mode 100644 index af959e00e..000000000 --- a/contracts/core/test/utils/simple_order_filled_cancelled_fetcher.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { AbstractOrderFilledCancelledFetcher, orderHashUtils } from '@0x/order-utils'; -import { SignedOrder } from '@0x/types'; -import { BigNumber } from '@0x/utils'; - -import { ExchangeWrapper } from './exchange_wrapper'; - -export class SimpleOrderFilledCancelledFetcher implements AbstractOrderFilledCancelledFetcher { - private readonly _exchangeWrapper: ExchangeWrapper; - private readonly _zrxAssetData: string; - constructor(exchange: ExchangeWrapper, zrxAssetData: string) { - this._exchangeWrapper = exchange; - this._zrxAssetData = zrxAssetData; - } - public async getFilledTakerAmountAsync(orderHash: string): Promise<BigNumber> { - const filledTakerAmount = new BigNumber(await this._exchangeWrapper.getTakerAssetFilledAmountAsync(orderHash)); - return filledTakerAmount; - } - public async isOrderCancelledAsync(signedOrder: SignedOrder): Promise<boolean> { - const orderHash = orderHashUtils.getOrderHashHex(signedOrder); - const isCancelled = await this._exchangeWrapper.isCancelledAsync(orderHash); - const orderEpoch = await this._exchangeWrapper.getOrderEpochAsync( - signedOrder.makerAddress, - signedOrder.senderAddress, - ); - const isCancelledByOrderEpoch = orderEpoch > signedOrder.salt; - return isCancelled || isCancelledByOrderEpoch; - } - public getZRXAssetData(): string { - return this._zrxAssetData; - } -} diff --git a/contracts/core/tsconfig.json b/contracts/core/tsconfig.json deleted file mode 100644 index db872fc32..000000000 --- a/contracts/core/tsconfig.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "extends": "../../tsconfig", - "compilerOptions": { - "outDir": "lib", - "rootDir": ".", - "resolveJsonModule": true - }, - "include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*"], - "files": [ - "./generated-artifacts/AssetProxyOwner.json", - "./generated-artifacts/ERC20Proxy.json", - "./generated-artifacts/ERC721Proxy.json", - "./generated-artifacts/Exchange.json", - "./generated-artifacts/MixinAuthorizable.json", - "./generated-artifacts/MultiAssetProxy.json", - "./generated-artifacts/TestAssetProxyDispatcher.json", - "./generated-artifacts/TestAssetProxyOwner.json", - "./generated-artifacts/TestExchangeInternals.json", - "./generated-artifacts/TestSignatureValidator.json", - "./generated-artifacts/TestStaticCallReceiver.json" - ], - "exclude": ["./deploy/solc/solc_bin"] -} diff --git a/contracts/core/tslint.json b/contracts/core/tslint.json deleted file mode 100644 index 1bb3ac2a2..000000000 --- a/contracts/core/tslint.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "extends": ["@0x/tslint-config"], - "rules": { - "custom-no-magic-numbers": false - } -} |