aboutsummaryrefslogtreecommitdiffstats
path: root/packages/contracts
diff options
context:
space:
mode:
Diffstat (limited to 'packages/contracts')
-rw-r--r--packages/contracts/compiler.json2
-rw-r--r--packages/contracts/package.json35
-rw-r--r--packages/contracts/src/contracts/current/protocol/AssetProxy/ERC20Proxy.sol2
-rw-r--r--packages/contracts/src/contracts/current/protocol/AssetProxy/ERC721Proxy.sol2
-rw-r--r--packages/contracts/src/contracts/current/protocol/AssetProxy/MixinAssetProxy.sol2
-rw-r--r--packages/contracts/src/contracts/current/protocol/AssetProxy/MixinAuthorizable.sol2
-rw-r--r--packages/contracts/src/contracts/current/protocol/AssetProxy/interfaces/IAssetProxy.sol2
-rw-r--r--packages/contracts/src/contracts/current/protocol/AssetProxy/interfaces/IAuthorizable.sol2
-rw-r--r--packages/contracts/src/contracts/current/protocol/AssetProxy/mixins/MAssetProxy.sol2
-rw-r--r--packages/contracts/src/contracts/current/protocol/AssetProxy/mixins/MAuthorizable.sol2
-rw-r--r--packages/contracts/src/contracts/current/protocol/Exchange/Exchange.sol17
-rw-r--r--packages/contracts/src/contracts/current/protocol/Exchange/MixinAssetProxyDispatcher.sol2
-rw-r--r--packages/contracts/src/contracts/current/protocol/Exchange/MixinExchangeCore.sol415
-rw-r--r--packages/contracts/src/contracts/current/protocol/Exchange/MixinMatchOrders.sol297
-rw-r--r--packages/contracts/src/contracts/current/protocol/Exchange/MixinSettlement.sol106
-rw-r--r--packages/contracts/src/contracts/current/protocol/Exchange/MixinSignatureValidator.sol2
-rw-r--r--packages/contracts/src/contracts/current/protocol/Exchange/MixinTransactions.sol2
-rw-r--r--packages/contracts/src/contracts/current/protocol/Exchange/MixinWrapperFunctions.sol7
-rw-r--r--packages/contracts/src/contracts/current/protocol/Exchange/interfaces/IAssetProxyDispatcher.sol2
-rw-r--r--packages/contracts/src/contracts/current/protocol/Exchange/interfaces/IExchange.sol18
-rw-r--r--packages/contracts/src/contracts/current/protocol/Exchange/interfaces/IExchangeCore.sol31
-rw-r--r--packages/contracts/src/contracts/current/protocol/Exchange/interfaces/IMatchOrders.sol44
-rw-r--r--packages/contracts/src/contracts/current/protocol/Exchange/interfaces/ISignatureValidator.sol2
-rw-r--r--packages/contracts/src/contracts/current/protocol/Exchange/interfaces/ISigner.sol2
-rw-r--r--packages/contracts/src/contracts/current/protocol/Exchange/interfaces/ITransactions.sol2
-rw-r--r--packages/contracts/src/contracts/current/protocol/Exchange/interfaces/IWrapperFunctions.sol6
-rw-r--r--packages/contracts/src/contracts/current/protocol/Exchange/libs/LibExchangeErrors.sol26
-rw-r--r--packages/contracts/src/contracts/current/protocol/Exchange/libs/LibFillResults.sol25
-rw-r--r--packages/contracts/src/contracts/current/protocol/Exchange/libs/LibMath.sol23
-rw-r--r--packages/contracts/src/contracts/current/protocol/Exchange/libs/LibOrder.sol11
-rw-r--r--packages/contracts/src/contracts/current/protocol/Exchange/libs/LibStatus.sol51
-rw-r--r--packages/contracts/src/contracts/current/protocol/Exchange/mixins/MAssetProxyDispatcher.sol3
-rw-r--r--packages/contracts/src/contracts/current/protocol/Exchange/mixins/MExchangeCore.sol61
-rw-r--r--packages/contracts/src/contracts/current/protocol/Exchange/mixins/MMatchOrders.sol65
-rw-r--r--packages/contracts/src/contracts/current/protocol/Exchange/mixins/MSettlement.sol33
-rw-r--r--packages/contracts/src/contracts/current/protocol/Exchange/mixins/MSignatureValidator.sol2
-rw-r--r--packages/contracts/src/contracts/current/protocol/Exchange/mixins/MTransactions.sol2
-rw-r--r--packages/contracts/src/contracts/current/test/DummyERC20Token/DummyERC20Token.sol2
-rw-r--r--packages/contracts/src/contracts/current/test/DummyERC721Token/DummyERC721Token.sol2
-rw-r--r--packages/contracts/src/contracts/current/test/Mintable/Mintable.sol2
-rw-r--r--packages/contracts/src/contracts/current/test/TestAssetProxyDispatcher/TestAssetProxyDispatcher.sol2
-rw-r--r--packages/contracts/src/contracts/current/test/TestLibBytes/TestLibBytes.sol2
-rw-r--r--packages/contracts/src/contracts/current/test/TestLibs/TestLibs.sol4
-rw-r--r--packages/contracts/src/contracts/current/test/TestSignatureValidator/TestSignatureValidator.sol4
-rw-r--r--packages/contracts/src/contracts/current/tokens/ERC20Token/ERC20Token.sol2
-rw-r--r--packages/contracts/src/contracts/current/tokens/ERC20Token/IERC20Token.sol2
-rw-r--r--packages/contracts/src/contracts/current/tokens/ERC721Token/ERC721Token.sol4
-rw-r--r--packages/contracts/src/contracts/current/tokens/ERC721Token/IERC721Receiver.sol4
-rw-r--r--packages/contracts/src/contracts/current/tokens/ERC721Token/IERC721Token.sol4
-rw-r--r--packages/contracts/src/contracts/current/tokens/UnlimitedAllowanceToken/UnlimitedAllowanceToken.sol2
-rw-r--r--packages/contracts/src/contracts/current/utils/LibBytes/LibBytes.sol2
-rw-r--r--packages/contracts/src/contracts/current/utils/Ownable/IOwnable.sol2
-rw-r--r--packages/contracts/src/contracts/current/utils/Ownable/Ownable.sol2
-rw-r--r--packages/contracts/src/contracts/current/utils/SafeMath/SafeMath.sol2
-rw-r--r--packages/contracts/src/utils/address_utils.ts4
-rw-r--r--packages/contracts/src/utils/asset_proxy_utils.ts107
-rw-r--r--packages/contracts/src/utils/constants.ts16
-rw-r--r--packages/contracts/src/utils/coverage.ts21
-rw-r--r--packages/contracts/src/utils/erc20_wrapper.ts8
-rw-r--r--packages/contracts/src/utils/erc721_wrapper.ts14
-rw-r--r--packages/contracts/src/utils/exchange_wrapper.ts37
-rw-r--r--packages/contracts/src/utils/formatters.ts8
-rw-r--r--packages/contracts/src/utils/match_order_tester.ts356
-rw-r--r--packages/contracts/src/utils/order_factory.ts9
-rw-r--r--packages/contracts/src/utils/order_utils.ts13
-rw-r--r--packages/contracts/src/utils/transaction_factory.ts4
-rw-r--r--packages/contracts/src/utils/types.ts78
-rw-r--r--packages/contracts/src/utils/web3_wrapper.ts13
-rw-r--r--packages/contracts/test/asset_proxy/authorizable.ts54
-rw-r--r--packages/contracts/test/asset_proxy/proxies.ts108
-rw-r--r--packages/contracts/test/ether_token.ts70
-rw-r--r--packages/contracts/test/exchange/core.ts161
-rw-r--r--packages/contracts/test/exchange/dispatcher.ts198
-rw-r--r--packages/contracts/test/exchange/libs.ts8
-rw-r--r--packages/contracts/test/exchange/match_orders.ts838
-rw-r--r--packages/contracts/test/exchange/signature_validator.ts16
-rw-r--r--packages/contracts/test/exchange/transactions.ts27
-rw-r--r--packages/contracts/test/exchange/wrapper.ts98
-rw-r--r--packages/contracts/test/global_hooks.ts4
-rw-r--r--packages/contracts/test/libraries/lib_bytes.ts8
-rw-r--r--packages/contracts/test/multi_sig_with_time_lock.ts67
-rw-r--r--packages/contracts/test/multi_sig_with_time_lock_except_remove_auth_addr.ts1
-rw-r--r--packages/contracts/test/token_registry.ts33
-rw-r--r--packages/contracts/test/tutorials/arbitrage.ts5
-rw-r--r--packages/contracts/test/unlimited_allowance_token.ts66
-rw-r--r--packages/contracts/test/zrx_token.ts67
-rw-r--r--packages/contracts/tslint.json5
87 files changed, 3224 insertions, 652 deletions
diff --git a/packages/contracts/compiler.json b/packages/contracts/compiler.json
index 9b0a1e161..bf6cc2376 100644
--- a/packages/contracts/compiler.json
+++ b/packages/contracts/compiler.json
@@ -4,7 +4,7 @@
"compilerSettings": {
"optimizer": {
"enabled": true,
- "runs": 200
+ "runs": 0
},
"outputSelection": {
"*": {
diff --git a/packages/contracts/package.json b/packages/contracts/package.json
index b10ed3c44..1c66a3b86 100644
--- a/packages/contracts/package.json
+++ b/packages/contracts/package.json
@@ -1,9 +1,9 @@
{
"private": true,
"name": "contracts",
- "version": "2.1.28",
+ "version": "2.1.29",
"engines": {
- "node" : ">=6.12"
+ "node": ">=6.12"
},
"description": "Smart contract components of 0x protocol",
"main": "index.js",
@@ -12,7 +12,7 @@
},
"scripts": {
"watch": "tsc -w",
- "prebuild": "run-s clean copy_artifacts generate_contract_wrappers",
+ "prebuild": "run-s clean compile copy_artifacts generate_contract_wrappers",
"copy_artifacts": "copyfiles -u 4 '../migrations/artifacts/2.0.0/**/*' ./lib/src/artifacts;",
"build": "tsc",
"test": "run-s build run_mocha",
@@ -28,8 +28,7 @@
"test:circleci": "yarn test:coverage"
},
"config": {
- "abis":
- "../migrations/artifacts/2.0.0/@(DummyERC20Token|DummyERC721Token|ERC20Proxy|ERC721Proxy|Exchange|MixinAuthorizable|MultiSigWallet|MultiSigWalletWithTimeLock|MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress|TestAssetProxyDispatcher|TestLibBytes|TestLibs|TestSignatureValidator|TokenRegistry|WETH9|ZRXToken).json"
+ "abis": "../migrations/artifacts/2.0.0/@(DummyERC20Token|DummyERC721Token|ERC20Proxy|ERC721Proxy|Exchange|MixinAuthorizable|MultiSigWallet|MultiSigWalletWithTimeLock|MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress|TestAssetProxyDispatcher|TestLibBytes|TestLibs|TestSignatureValidator|TokenRegistry|WETH9|ZRXToken).json"
},
"repository": {
"type": "git",
@@ -42,9 +41,11 @@
},
"homepage": "https://github.com/0xProject/0x-monorepo/packages/contracts/README.md",
"devDependencies": {
- "@0xproject/abi-gen": "^0.2.13",
- "@0xproject/dev-utils": "^0.4.1",
- "@0xproject/tslint-config": "^0.4.17",
+ "@0xproject/abi-gen": "^0.3.0",
+ "@0xproject/dev-utils": "^0.4.2",
+ "@0xproject/tslint-config": "^0.4.18",
+ "@0xproject/subproviders": "^0.10.1",
+ "@0xproject/sol-cov": "^0.0.11",
"@types/lodash": "4.14.104",
"@types/node": "^8.0.53",
"@types/yargs": "^10.0.0",
@@ -53,23 +54,25 @@
"chai-bignumber": "^2.0.1",
"copyfiles": "^1.2.0",
"dirty-chai": "^2.0.1",
+ "make-promises-safe": "^1.1.0",
"mocha": "^4.0.1",
"npm-run-all": "^4.1.2",
"prettier": "^1.11.1",
"shx": "^0.2.2",
- "solc": "^0.4.23",
+ "solc": "^0.4.24",
"tslint": "5.8.0",
"typescript": "2.7.1",
"yargs": "^10.0.3"
},
"dependencies": {
- "0x.js": "^0.37.2",
- "@0xproject/base-contract": "^0.3.1",
- "@0xproject/sol-compiler": "^0.4.3",
- "@0xproject/types": "^0.6.3",
- "@0xproject/typescript-typings": "^0.3.1",
- "@0xproject/utils": "^0.6.1",
- "@0xproject/web3-wrapper": "^0.6.3",
+ "@0xproject/base-contract": "^0.3.2",
+ "@0xproject/contract-wrappers": "^0.0.2",
+ "@0xproject/order-utils": "^0.0.5",
+ "@0xproject/sol-compiler": "^0.5.0",
+ "@0xproject/types": "^0.7.0",
+ "@0xproject/typescript-typings": "^0.3.2",
+ "@0xproject/utils": "^0.6.2",
+ "@0xproject/web3-wrapper": "^0.6.4",
"bn.js": "^4.11.8",
"ethereumjs-abi": "^0.6.4",
"ethereumjs-util": "^5.1.1",
diff --git a/packages/contracts/src/contracts/current/protocol/AssetProxy/ERC20Proxy.sol b/packages/contracts/src/contracts/current/protocol/AssetProxy/ERC20Proxy.sol
index c02536d67..ee0c66fdc 100644
--- a/packages/contracts/src/contracts/current/protocol/AssetProxy/ERC20Proxy.sol
+++ b/packages/contracts/src/contracts/current/protocol/AssetProxy/ERC20Proxy.sol
@@ -16,7 +16,7 @@
*/
-pragma solidity ^0.4.23;
+pragma solidity ^0.4.24;
pragma experimental ABIEncoderV2;
import "../../utils/LibBytes/LibBytes.sol";
diff --git a/packages/contracts/src/contracts/current/protocol/AssetProxy/ERC721Proxy.sol b/packages/contracts/src/contracts/current/protocol/AssetProxy/ERC721Proxy.sol
index 475359087..94aab9139 100644
--- a/packages/contracts/src/contracts/current/protocol/AssetProxy/ERC721Proxy.sol
+++ b/packages/contracts/src/contracts/current/protocol/AssetProxy/ERC721Proxy.sol
@@ -16,7 +16,7 @@
*/
-pragma solidity ^0.4.23;
+pragma solidity ^0.4.24;
pragma experimental ABIEncoderV2;
import "../../utils/LibBytes/LibBytes.sol";
diff --git a/packages/contracts/src/contracts/current/protocol/AssetProxy/MixinAssetProxy.sol b/packages/contracts/src/contracts/current/protocol/AssetProxy/MixinAssetProxy.sol
index d58cfc2dd..4ec31304f 100644
--- a/packages/contracts/src/contracts/current/protocol/AssetProxy/MixinAssetProxy.sol
+++ b/packages/contracts/src/contracts/current/protocol/AssetProxy/MixinAssetProxy.sol
@@ -16,7 +16,7 @@
*/
-pragma solidity ^0.4.23;
+pragma solidity ^0.4.24;
pragma experimental ABIEncoderV2;
import "./mixins/MAssetProxy.sol";
diff --git a/packages/contracts/src/contracts/current/protocol/AssetProxy/MixinAuthorizable.sol b/packages/contracts/src/contracts/current/protocol/AssetProxy/MixinAuthorizable.sol
index b66b783ea..0bbd3b318 100644
--- a/packages/contracts/src/contracts/current/protocol/AssetProxy/MixinAuthorizable.sol
+++ b/packages/contracts/src/contracts/current/protocol/AssetProxy/MixinAuthorizable.sol
@@ -16,7 +16,7 @@
*/
-pragma solidity ^0.4.23;
+pragma solidity ^0.4.24;
pragma experimental ABIEncoderV2;
import "./mixins/MAuthorizable.sol";
diff --git a/packages/contracts/src/contracts/current/protocol/AssetProxy/interfaces/IAssetProxy.sol b/packages/contracts/src/contracts/current/protocol/AssetProxy/interfaces/IAssetProxy.sol
index eca6524e5..8b30dfabb 100644
--- a/packages/contracts/src/contracts/current/protocol/AssetProxy/interfaces/IAssetProxy.sol
+++ b/packages/contracts/src/contracts/current/protocol/AssetProxy/interfaces/IAssetProxy.sol
@@ -16,7 +16,7 @@
*/
-pragma solidity ^0.4.23;
+pragma solidity ^0.4.24;
pragma experimental ABIEncoderV2;
import "./IAuthorizable.sol";
diff --git a/packages/contracts/src/contracts/current/protocol/AssetProxy/interfaces/IAuthorizable.sol b/packages/contracts/src/contracts/current/protocol/AssetProxy/interfaces/IAuthorizable.sol
index 3120be7ec..d6fe03898 100644
--- a/packages/contracts/src/contracts/current/protocol/AssetProxy/interfaces/IAuthorizable.sol
+++ b/packages/contracts/src/contracts/current/protocol/AssetProxy/interfaces/IAuthorizable.sol
@@ -16,7 +16,7 @@
*/
-pragma solidity ^0.4.23;
+pragma solidity ^0.4.24;
pragma experimental ABIEncoderV2;
import "../../../utils/Ownable/IOwnable.sol";
diff --git a/packages/contracts/src/contracts/current/protocol/AssetProxy/mixins/MAssetProxy.sol b/packages/contracts/src/contracts/current/protocol/AssetProxy/mixins/MAssetProxy.sol
index e0ec8c4e1..3800bf04c 100644
--- a/packages/contracts/src/contracts/current/protocol/AssetProxy/mixins/MAssetProxy.sol
+++ b/packages/contracts/src/contracts/current/protocol/AssetProxy/mixins/MAssetProxy.sol
@@ -16,7 +16,7 @@
*/
-pragma solidity ^0.4.23;
+pragma solidity ^0.4.24;
pragma experimental ABIEncoderV2;
import "../interfaces/IAssetProxy.sol";
diff --git a/packages/contracts/src/contracts/current/protocol/AssetProxy/mixins/MAuthorizable.sol b/packages/contracts/src/contracts/current/protocol/AssetProxy/mixins/MAuthorizable.sol
index 71d1910e5..cdf60bdee 100644
--- a/packages/contracts/src/contracts/current/protocol/AssetProxy/mixins/MAuthorizable.sol
+++ b/packages/contracts/src/contracts/current/protocol/AssetProxy/mixins/MAuthorizable.sol
@@ -16,7 +16,7 @@
*/
-pragma solidity ^0.4.23;
+pragma solidity ^0.4.24;
pragma experimental ABIEncoderV2;
import "../interfaces/IAuthorizable.sol";
diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/Exchange.sol b/packages/contracts/src/contracts/current/protocol/Exchange/Exchange.sol
index 85e2bc47e..b7b308069 100644
--- a/packages/contracts/src/contracts/current/protocol/Exchange/Exchange.sol
+++ b/packages/contracts/src/contracts/current/protocol/Exchange/Exchange.sol
@@ -16,7 +16,7 @@
*/
-pragma solidity ^0.4.23;
+pragma solidity ^0.4.24;
pragma experimental ABIEncoderV2;
import "./MixinExchangeCore.sol";
@@ -25,24 +25,29 @@ import "./MixinSettlement.sol";
import "./MixinWrapperFunctions.sol";
import "./MixinAssetProxyDispatcher.sol";
import "./MixinTransactions.sol";
+import "./MixinMatchOrders.sol";
contract Exchange is
- MixinWrapperFunctions,
MixinExchangeCore,
+ MixinMatchOrders,
MixinSettlement,
MixinSignatureValidator,
MixinTransactions,
- MixinAssetProxyDispatcher
+ MixinAssetProxyDispatcher,
+ MixinWrapperFunctions
{
+
string constant public VERSION = "2.0.1-alpha";
+ // Mixins are instantiated in the order they are inherited
constructor (bytes memory _zrxProxyData)
public
MixinExchangeCore()
- MixinSignatureValidator()
+ MixinMatchOrders()
MixinSettlement(_zrxProxyData)
- MixinWrapperFunctions()
- MixinAssetProxyDispatcher()
+ MixinSignatureValidator()
MixinTransactions()
+ MixinAssetProxyDispatcher()
+ MixinWrapperFunctions()
{}
}
diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/MixinAssetProxyDispatcher.sol b/packages/contracts/src/contracts/current/protocol/Exchange/MixinAssetProxyDispatcher.sol
index 308dace32..3b38d1f37 100644
--- a/packages/contracts/src/contracts/current/protocol/Exchange/MixinAssetProxyDispatcher.sol
+++ b/packages/contracts/src/contracts/current/protocol/Exchange/MixinAssetProxyDispatcher.sol
@@ -16,7 +16,7 @@
*/
-pragma solidity ^0.4.23;
+pragma solidity ^0.4.24;
import "../../utils/Ownable/Ownable.sol";
import "../AssetProxy/interfaces/IAssetProxy.sol";
diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/MixinExchangeCore.sol b/packages/contracts/src/contracts/current/protocol/Exchange/MixinExchangeCore.sol
index 82c7a2ddc..1c2420374 100644
--- a/packages/contracts/src/contracts/current/protocol/Exchange/MixinExchangeCore.sol
+++ b/packages/contracts/src/contracts/current/protocol/Exchange/MixinExchangeCore.sol
@@ -16,12 +16,13 @@
*/
-pragma solidity ^0.4.23;
+pragma solidity ^0.4.24;
pragma experimental ABIEncoderV2;
import "./libs/LibFillResults.sol";
import "./libs/LibOrder.sol";
import "./libs/LibMath.sol";
+import "./libs/LibStatus.sol";
import "./libs/LibExchangeErrors.sol";
import "./mixins/MExchangeCore.sol";
import "./mixins/MSettlement.sol";
@@ -29,9 +30,11 @@ import "./mixins/MSignatureValidator.sol";
import "./mixins/MTransactions.sol";
contract MixinExchangeCore is
+ SafeMath,
+ LibMath,
+ LibStatus,
LibOrder,
LibFillResults,
- LibMath,
LibExchangeErrors,
MExchangeCore,
MSettlement,
@@ -48,6 +51,8 @@ contract MixinExchangeCore is
// Orders with a salt less than their maker's epoch are considered cancelled
mapping (address => uint256) public makerEpoch;
+ ////// Core exchange functions //////
+
/// @dev Cancels all orders reated by sender with a salt less than or equal to the specified salt value.
/// @param salt Orders created with a salt less or equal to this value will be cancelled.
function cancelOrdersUpTo(uint256 salt)
@@ -70,42 +75,243 @@ contract MixinExchangeCore is
function fillOrder(
Order memory order,
uint256 takerAssetFillAmount,
- bytes memory signature)
+ bytes memory signature
+ )
public
returns (FillResults memory fillResults)
{
+ // Fetch order info
+ OrderInfo memory orderInfo = getOrderInfo(order);
+
+ // Fetch taker address
+ address takerAddress = getCurrentContextAddress();
+
+ // Either our context is valid or we revert
+ assertValidFill(
+ order,
+ orderInfo.orderStatus,
+ orderInfo.orderHash,
+ takerAddress,
+ orderInfo.orderTakerAssetFilledAmount,
+ takerAssetFillAmount,
+ signature
+ );
+
+ // Compute proportional fill amounts
+ uint8 status;
+ (status, fillResults) = calculateFillResults(
+ order,
+ orderInfo.orderStatus,
+ orderInfo.orderTakerAssetFilledAmount,
+ takerAssetFillAmount
+ );
+ if (status != uint8(Status.SUCCESS)) {
+ emit ExchangeStatus(uint8(status), orderInfo.orderHash);
+ return getNullFillResults();
+ }
+
+ // Settle order
+ settleOrder(order, takerAddress, fillResults);
+
+ // Update exchange internal state
+ updateFilledState(
+ order,
+ takerAddress,
+ orderInfo.orderHash,
+ orderInfo.orderTakerAssetFilledAmount,
+ 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 Status.FILLABLE.
+ /// @return True if the order state changed to cancelled.
+ /// False if the order was valid, but in an
+ /// unfillable state (see LibStatus.STATUS for order states)
+ function cancelOrder(Order memory order)
+ public
+ returns (bool)
+ {
+ // Fetch current order status
+ OrderInfo memory orderInfo = getOrderInfo(order);
+
+ // Validate context
+ assertValidCancel(order, orderInfo.orderStatus, orderInfo.orderHash);
+
+ // Perform cancel
+ return updateCancelledState(order, orderInfo.orderStatus, orderInfo.orderHash);
+ }
+
+ /// @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 (LibOrder.OrderInfo memory orderInfo)
+ {
// Compute the order hash
- bytes32 orderHash = getOrderHash(order);
+ orderInfo.orderHash = getOrderHash(order);
- // Check if order has been cancelled by salt value
- if (order.salt < makerEpoch[order.makerAddress]) {
- emit ExchangeError(uint8(Errors.ORDER_CANCELLED), orderHash);
- return fillResults;
+ // 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(Status.ORDER_INVALID_MAKER_ASSET_AMOUNT);
+ return orderInfo;
}
- // Check if order has been cancelled by orderHash
- if (cancelled[orderHash]) {
- emit ExchangeError(uint8(Errors.ORDER_CANCELLED), orderHash);
- return fillResults;
+ // 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(Status.ORDER_INVALID_TAKER_ASSET_AMOUNT);
+ return orderInfo;
}
- // Validate order and maker only if first time seen
- // TODO: Read filled and cancelled only once
- if (filled[orderHash] == 0) {
- require(
- order.makerAssetAmount > 0,
- GT_ZERO_AMOUNT_REQUIRED
- );
- require(
- order.takerAssetAmount > 0,
- GT_ZERO_AMOUNT_REQUIRED
- );
+ // Validate order expiration
+ if (block.timestamp >= order.expirationTimeSeconds) {
+ orderInfo.orderStatus = uint8(Status.ORDER_EXPIRED);
+ return orderInfo;
+ }
+
+ // Check if order has been cancelled
+ if (cancelled[orderInfo.orderHash]) {
+ orderInfo.orderStatus = uint8(Status.ORDER_CANCELLED);
+ return orderInfo;
+ }
+ if (makerEpoch[order.makerAddress] > order.salt) {
+ orderInfo.orderStatus = uint8(Status.ORDER_CANCELLED);
+ return orderInfo;
+ }
+
+ // Fetch filled amount and validate order availability
+ orderInfo.orderTakerAssetFilledAmount = filled[orderInfo.orderHash];
+ if (orderInfo.orderTakerAssetFilledAmount >= order.takerAssetAmount) {
+ orderInfo.orderStatus = uint8(Status.ORDER_FULLY_FILLED);
+ return orderInfo;
+ }
+
+ // All other statuses are ruled out: order is Fillable
+ orderInfo.orderStatus = uint8(Status.ORDER_FILLABLE);
+ return orderInfo;
+ }
+
+ /// @dev Calculates amounts filled and fees paid by maker and taker.
+ /// @param order to be filled.
+ /// @param orderStatus Status of order to be filled.
+ /// @param orderTakerAssetFilledAmount Amount of order already filled.
+ /// @param takerAssetFillAmount Desired amount of order to fill by taker.
+ /// @return status Return status of calculating fill amounts. Returns Status.SUCCESS on success.
+ /// @return fillResults Amounts filled and fees paid by maker and taker.
+ function calculateFillResults(
+ Order memory order,
+ uint8 orderStatus,
+ uint256 orderTakerAssetFilledAmount,
+ uint256 takerAssetFillAmount
+ )
+ public
+ pure
+ returns (
+ uint8 status,
+ FillResults memory fillResults
+ )
+ {
+ // Fill amount must be greater than 0
+ if (takerAssetFillAmount == 0) {
+ status = uint8(Status.TAKER_ASSET_FILL_AMOUNT_TOO_LOW);
+ return (status, fillResults);
+ }
+
+ // Ensure the order is fillable
+ if (orderStatus != uint8(Status.ORDER_FILLABLE)) {
+ status = orderStatus;
+ return (status, fillResults);
+ }
+
+ // Compute takerAssetFilledAmount
+ uint256 remainingTakerAssetAmount = safeSub(order.takerAssetAmount, orderTakerAssetFilledAmount);
+ uint256 takerAssetFilledAmount = min256(takerAssetFillAmount, remainingTakerAssetAmount);
+
+ // Validate fill order rounding
+ if (isRoundingError(
+ takerAssetFilledAmount,
+ order.takerAssetAmount,
+ order.makerAssetAmount))
+ {
+ status = uint8(Status.ROUNDING_ERROR_TOO_LARGE);
+ return (status, fillResults);
+ }
+
+ // Compute proportional transfer amounts
+ // TODO: All three are multiplied by the same fraction. This can
+ // potentially be optimized.
+ fillResults.takerAssetFilledAmount = takerAssetFilledAmount;
+ fillResults.makerAssetFilledAmount = getPartialAmount(
+ fillResults.takerAssetFilledAmount,
+ order.takerAssetAmount,
+ order.makerAssetAmount
+ );
+ fillResults.makerFeePaid = getPartialAmount(
+ fillResults.takerAssetFilledAmount,
+ order.takerAssetAmount,
+ order.makerFee
+ );
+ fillResults.takerFeePaid = getPartialAmount(
+ fillResults.takerAssetFilledAmount,
+ order.takerAssetAmount,
+ order.takerFee
+ );
+
+ status = uint8(Status.SUCCESS);
+ return (status, fillResults);
+ }
+
+ /// @dev Validates context for fillOrder. Succeeds or throws.
+ /// @param order to be filled.
+ /// @param orderStatus Status of order to be filled.
+ /// @param orderHash Hash of order to be filled.
+ /// @param takerAddress Address of order taker.
+ /// @param orderTakerAssetFilledAmount Amount of order already filled.
+ /// @param takerAssetFillAmount Desired amount of order to fill by taker.
+ /// @param signature Proof that the orders was created by its maker.
+ function assertValidFill(
+ Order memory order,
+ uint8 orderStatus,
+ bytes32 orderHash,
+ address takerAddress,
+ uint256 orderTakerAssetFilledAmount,
+ uint256 takerAssetFillAmount,
+ bytes memory signature
+ )
+ internal
+ {
+ // Ensure order is valid
+ // An order can only be filled if its status is FILLABLE;
+ // however, only invalid statuses result in a throw.
+ // See LibStatus for a complete description of order statuses.
+ require(
+ orderStatus != uint8(Status.ORDER_INVALID_MAKER_ASSET_AMOUNT),
+ INVALID_ORDER_MAKER_ASSET_AMOUNT
+ );
+ require(
+ orderStatus != uint8(Status.ORDER_INVALID_TAKER_ASSET_AMOUNT),
+ INVALID_ORDER_TAKER_ASSET_AMOUNT
+ );
+
+ // Validate Maker signature (check only if first time seen)
+ if (orderTakerAssetFilledAmount == 0) {
require(
isValidSignature(orderHash, order.makerAddress, signature),
SIGNATURE_VALIDATION_FAILED
);
}
-
+
// Validate sender is allowed to fill this order
if (order.senderAddress != address(0)) {
require(
@@ -115,7 +321,6 @@ contract MixinExchangeCore is
}
// Validate taker is allowed to fill this order
- address takerAddress = getCurrentContextAddress();
if (order.takerAddress != address(0)) {
require(
order.takerAddress == takerAddress,
@@ -126,88 +331,108 @@ contract MixinExchangeCore is
takerAssetFillAmount > 0,
GT_ZERO_AMOUNT_REQUIRED
);
+ }
- // Validate order expiration
- if (block.timestamp >= order.expirationTimeSeconds) {
- emit ExchangeError(uint8(Errors.ORDER_EXPIRED), orderHash);
- return fillResults;
- }
-
- // Validate order availability
- uint256 remainingTakerAssetFillAmount = safeSub(order.takerAssetAmount, filled[orderHash]);
- if (remainingTakerAssetFillAmount == 0) {
- emit ExchangeError(uint8(Errors.ORDER_FULLY_FILLED), orderHash);
- return fillResults;
- }
-
- // Validate fill order rounding
- fillResults.takerAssetFilledAmount = min256(takerAssetFillAmount, remainingTakerAssetFillAmount);
- if (isRoundingError(fillResults.takerAssetFilledAmount, order.takerAssetAmount, order.makerAssetAmount)) {
- emit ExchangeError(uint8(Errors.ROUNDING_ERROR_TOO_LARGE), orderHash);
- fillResults.takerAssetFilledAmount = 0;
- return fillResults;
- }
-
+ /// @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(
+ Order memory order,
+ address takerAddress,
+ bytes32 orderHash,
+ uint256 orderTakerAssetFilledAmount,
+ FillResults memory fillResults
+ )
+ internal
+ {
// Update state
- filled[orderHash] = safeAdd(filled[orderHash], fillResults.takerAssetFilledAmount);
-
- // Settle order
- (fillResults.makerAssetFilledAmount, fillResults.makerFeePaid, fillResults.takerFeePaid) =
- settleOrder(order, takerAddress, fillResults.takerAssetFilledAmount);
+ filled[orderHash] = safeAdd(orderTakerAssetFilledAmount, fillResults.takerAssetFilledAmount);
// Log order
- emitFillEvent(order, takerAddress, orderHash, fillResults);
- return fillResults;
+ emit Fill(
+ order.makerAddress,
+ takerAddress,
+ order.feeRecipientAddress,
+ fillResults.makerAssetFilledAmount,
+ fillResults.takerAssetFilledAmount,
+ fillResults.makerFeePaid,
+ fillResults.takerFeePaid,
+ orderHash,
+ order.makerAssetData,
+ order.takerAssetData
+ );
}
- /// @dev After calling, the order can not be filled anymore.
- /// @param order Order struct containing order specifications.
- /// @return True if the order state changed to cancelled.
- /// False if the transaction was already cancelled or expired.
- function cancelOrder(Order memory order)
- public
- returns (bool)
+ /// @dev Validates context for cancelOrder. Succeeds or throws.
+ /// @param order that was cancelled.
+ /// @param orderStatus Status of order that was cancelled.
+ /// @param orderHash Hash of order that was cancelled.
+ function assertValidCancel(
+ Order memory order,
+ uint8 orderStatus,
+ bytes32 orderHash
+ )
+ internal
{
- // Compute the order hash
- bytes32 orderHash = getOrderHash(order);
-
- // Validate the order
+ // Ensure order is valid
+ // An order can only be cancelled if its status is FILLABLE;
+ // however, only invalid statuses result in a throw.
+ // See LibStatus for a complete description of order statuses.
require(
- order.makerAssetAmount > 0,
- GT_ZERO_AMOUNT_REQUIRED
+ orderStatus != uint8(Status.ORDER_INVALID_MAKER_ASSET_AMOUNT),
+ INVALID_ORDER_MAKER_ASSET_AMOUNT
);
require(
- order.takerAssetAmount > 0,
- GT_ZERO_AMOUNT_REQUIRED
+ orderStatus != uint8(Status.ORDER_INVALID_TAKER_ASSET_AMOUNT),
+ INVALID_ORDER_TAKER_ASSET_AMOUNT
);
- // 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_CONTEXT
);
-
- if (block.timestamp >= order.expirationTimeSeconds) {
- emit ExchangeError(uint8(Errors.ORDER_EXPIRED), orderHash);
- return false;
+
+ // Validate sender is allowed to cancel this order
+ if (order.senderAddress != address(0)) {
+ require(
+ order.senderAddress == msg.sender,
+ INVALID_SENDER
+ );
}
+ }
- if (cancelled[orderHash]) {
- emit ExchangeError(uint8(Errors.ORDER_CANCELLED), orderHash);
- return false;
+ /// @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 orderStatus Status of order that was cancelled.
+ /// @param orderHash Hash of order that was cancelled.
+ /// @return stateUpdated Returns true only if state was updated.
+ function updateCancelledState(
+ Order memory order,
+ uint8 orderStatus,
+ bytes32 orderHash
+ )
+ internal
+ returns (bool stateUpdated)
+ {
+ // Ensure order is fillable (otherwise cancelling does nothing)
+ // See LibStatus for a complete description of order statuses.
+ if (orderStatus != uint8(Status.ORDER_FILLABLE)) {
+ emit ExchangeStatus(uint8(orderStatus), orderHash);
+ stateUpdated = false;
+ return stateUpdated;
}
+ // Perform cancel
cancelled[orderHash] = true;
+ stateUpdated = true;
+ // Log cancel
emit Cancel(
order.makerAddress,
order.feeRecipientAddress,
@@ -215,29 +440,7 @@ contract MixinExchangeCore is
order.makerAssetData,
order.takerAssetData
);
- return true;
- }
- /// @dev Logs a Fill event with the given arguments.
- /// The sole purpose of this function is to get around the stack variable limit.
- function emitFillEvent(
- Order memory order,
- address takerAddress,
- bytes32 orderHash,
- FillResults memory fillResults)
- internal
- {
- emit Fill(
- order.makerAddress,
- takerAddress,
- order.feeRecipientAddress,
- fillResults.makerAssetFilledAmount,
- fillResults.takerAssetFilledAmount,
- fillResults.makerFeePaid,
- fillResults.takerFeePaid,
- orderHash,
- order.makerAssetData,
- order.takerAssetData
- );
+ return stateUpdated;
}
}
diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/MixinMatchOrders.sol b/packages/contracts/src/contracts/current/protocol/Exchange/MixinMatchOrders.sol
new file mode 100644
index 000000000..9d8b521c0
--- /dev/null
+++ b/packages/contracts/src/contracts/current/protocol/Exchange/MixinMatchOrders.sol
@@ -0,0 +1,297 @@
+/*
+ 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 "./mixins/MExchangeCore.sol";
+import "./mixins/MMatchOrders.sol";
+import "./mixins/MSettlement.sol";
+import "./mixins/MTransactions.sol";
+import "../../utils/SafeMath/SafeMath.sol";
+import "./libs/LibMath.sol";
+import "./libs/LibOrder.sol";
+import "./libs/LibStatus.sol";
+import "../../utils/LibBytes/LibBytes.sol";
+import "./libs/LibExchangeErrors.sol";
+
+contract MixinMatchOrders is
+ SafeMath,
+ LibBytes,
+ LibMath,
+ LibStatus,
+ LibOrder,
+ LibFillResults,
+ LibExchangeErrors,
+ MExchangeCore,
+ MMatchOrders,
+ MSettlement,
+ 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.
+ /// TODO: Make this function external once supported by Solidity (See Solidity Issues #3199, #1603)
+ function matchOrders(
+ Order memory leftOrder,
+ Order memory rightOrder,
+ bytes memory leftSignature,
+ bytes memory rightSignature
+ )
+ public
+ returns (MatchedFillResults memory matchedFillResults)
+ {
+ // Get left & right order info
+ OrderInfo memory leftOrderInfo = getOrderInfo(leftOrder);
+ OrderInfo memory rightOrderInfo = getOrderInfo(rightOrder);
+
+ // Fetch taker address
+ address takerAddress = getCurrentContextAddress();
+
+ // Either our context is valid or we revert
+ assertValidMatch(leftOrder, rightOrder);
+
+ // Compute proportional fill amounts
+ matchedFillResults = calculateMatchedFillResults(
+ leftOrder,
+ rightOrder,
+ leftOrderInfo.orderStatus,
+ rightOrderInfo.orderStatus,
+ leftOrderInfo.orderTakerAssetFilledAmount,
+ rightOrderInfo.orderTakerAssetFilledAmount
+ );
+
+ // Validate fill contexts
+ assertValidFill(
+ leftOrder,
+ leftOrderInfo.orderStatus,
+ leftOrderInfo.orderHash,
+ takerAddress,
+ leftOrderInfo.orderTakerAssetFilledAmount,
+ matchedFillResults.left.takerAssetFilledAmount,
+ leftSignature
+ );
+ assertValidFill(
+ rightOrder,
+ rightOrderInfo.orderStatus,
+ rightOrderInfo.orderHash,
+ takerAddress,
+ rightOrderInfo.orderTakerAssetFilledAmount,
+ matchedFillResults.right.takerAssetFilledAmount,
+ rightSignature
+ );
+
+ // Settle matched orders. Succeeds or throws.
+ settleMatchedOrders(
+ leftOrder,
+ rightOrder,
+ takerAddress,
+ matchedFillResults
+ );
+
+ // Update exchange state
+ updateFilledState(
+ leftOrder,
+ takerAddress,
+ leftOrderInfo.orderHash,
+ leftOrderInfo.orderTakerAssetFilledAmount,
+ matchedFillResults.left
+ );
+ updateFilledState(
+ rightOrder,
+ takerAddress,
+ rightOrderInfo.orderHash,
+ rightOrderInfo.orderTakerAssetFilledAmount,
+ matchedFillResults.right
+ );
+
+ return matchedFillResults;
+ }
+
+ /// @dev Validates context for matchOrders. Succeeds or throws.
+ /// @param leftOrder First order to match.
+ /// @param rightOrder Second order to match.
+ function assertValidMatch(
+ Order memory leftOrder,
+ Order memory rightOrder
+ )
+ internal
+ {
+ // The leftOrder maker asset must be the same as the rightOrder taker asset.
+ // TODO: Can we safely assume equality and expect a later failure otherwise?
+ require(
+ areBytesEqual(leftOrder.makerAssetData, rightOrder.takerAssetData),
+ ASSET_MISMATCH_MAKER_TAKER
+ );
+
+ // The leftOrder taker asset must be the same as the rightOrder maker asset.
+ // TODO: Can we safely assume equality and expect a later failure otherwise?
+ require(
+ areBytesEqual(leftOrder.takerAssetData, rightOrder.makerAssetData),
+ ASSET_MISMATCH_TAKER_MAKER
+ );
+
+ // 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
+ );
+ }
+
+ /// @dev Validates matched fill results. Succeeds or throws.
+ /// @param matchedFillResults Amounts to fill and fees to pay by maker and taker of matched orders.
+ function assertValidMatchResults(MatchedFillResults memory matchedFillResults)
+ internal
+ {
+ // If the amount transferred from the left order is different than what is transferred, it is a rounding error amount.
+ // Ensure this difference is negligible by dividing the values with each other. The result should equal to ~1.
+ uint256 amountSpentByLeft = safeAdd(
+ matchedFillResults.right.takerAssetFilledAmount,
+ matchedFillResults.takerFillAmount
+ );
+ require(
+ !isRoundingError(
+ matchedFillResults.left.makerAssetFilledAmount,
+ amountSpentByLeft,
+ 1
+ ),
+ ROUNDING_ERROR_TRANSFER_AMOUNTS
+ );
+
+ // If the amount transferred from the right order is different than what is transferred, it is a rounding error amount.
+ // Ensure this difference is negligible by dividing the values with each other. The result should equal to ~1.
+ require(
+ !isRoundingError(
+ matchedFillResults.right.makerAssetFilledAmount,
+ matchedFillResults.left.takerAssetFilledAmount,
+ 1
+ ),
+ ROUNDING_ERROR_TRANSFER_AMOUNTS
+ );
+ }
+
+ /// @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 leftOrderStatus Order status of left order.
+ /// @param rightOrderStatus Order status of right order.
+ /// @param leftOrderFilledAmount Amount of left order already filled.
+ /// @param rightOrderFilledAmount Amount of right order already filled.
+ /// @param matchedFillResults Amounts to fill and fees to pay by maker and taker of matched orders.
+ function calculateMatchedFillResults(
+ Order memory leftOrder,
+ Order memory rightOrder,
+ uint8 leftOrderStatus,
+ uint8 rightOrderStatus,
+ uint256 leftOrderFilledAmount,
+ uint256 rightOrderFilledAmount
+ )
+ internal
+ returns (MatchedFillResults memory matchedFillResults)
+ {
+ // We settle orders at the exchange rate of the right order.
+ // The amount saved by the left maker goes to the taker.
+ // Either the left or right order will be fully filled; possibly both.
+ // The left order is fully filled iff the right order can sell more than left can buy.
+ // That is: the amount required to fill the left order is less than or equal to
+ // the amount we can spend from the right order:
+ // <leftTakerAssetAmountRemaining> <= <rightTakerAssetAmountRemaining> * <rightMakerToTakerRatio>
+ // <leftTakerAssetAmountRemaining> <= <rightTakerAssetAmountRemaining> * <rightOrder.makerAssetAmount> / <rightOrder.takerAssetAmount>
+ // <leftTakerAssetAmountRemaining> * <rightOrder.takerAssetAmount> <= <rightTakerAssetAmountRemaining> * <rightOrder.makerAssetAmount>
+ uint256 rightTakerAssetAmountRemaining = safeSub(rightOrder.takerAssetAmount, rightOrderFilledAmount);
+ uint256 leftTakerAssetAmountRemaining = safeSub(leftOrder.takerAssetAmount, leftOrderFilledAmount);
+ uint256 leftOrderAmountToFill;
+ uint256 rightOrderAmountToFill;
+ if (
+ safeMul(leftTakerAssetAmountRemaining, rightOrder.takerAssetAmount) <=
+ safeMul(rightTakerAssetAmountRemaining, rightOrder.makerAssetAmount)
+ ) {
+ // Left order will be fully filled: maximally fill left
+ leftOrderAmountToFill = leftTakerAssetAmountRemaining;
+
+ // The right order receives an amount proportional to how much was spent.
+ // TODO: Can we ensure rounding error is in the correct direction?
+ rightOrderAmountToFill = safeGetPartialAmount(
+ rightOrder.takerAssetAmount,
+ rightOrder.makerAssetAmount,
+ leftOrderAmountToFill
+ );
+ } else {
+ // Right order will be fully filled: maximally fill right
+ rightOrderAmountToFill = rightTakerAssetAmountRemaining;
+
+ // The left order receives an amount proportional to how much was spent.
+ // TODO: Can we ensure rounding error is in the correct direction?
+ leftOrderAmountToFill = safeGetPartialAmount(
+ rightOrder.makerAssetAmount,
+ rightOrder.takerAssetAmount,
+ rightOrderAmountToFill
+ );
+ }
+
+ // Calculate fill results for left order
+ uint8 status;
+ (status, matchedFillResults.left) = calculateFillResults(
+ leftOrder,
+ leftOrderStatus,
+ leftOrderFilledAmount,
+ leftOrderAmountToFill
+ );
+ require(
+ status == uint8(Status.SUCCESS),
+ FAILED_TO_CALCULATE_FILL_RESULTS_FOR_LEFT_ORDER
+ );
+
+ // Calculate fill results for right order
+ (status, matchedFillResults.right) = calculateFillResults(
+ rightOrder,
+ rightOrderStatus,
+ rightOrderFilledAmount,
+ rightOrderAmountToFill
+ );
+ require(
+ status == uint8(Status.SUCCESS),
+ FAILED_TO_CALCULATE_FILL_RESULTS_FOR_RIGHT_ORDER
+ );
+
+ // Calculate amount given to taker
+ matchedFillResults.takerFillAmount = safeSub(
+ matchedFillResults.left.makerAssetFilledAmount,
+ matchedFillResults.right.takerAssetFilledAmount
+ );
+
+ // Validate the fill results
+ assertValidMatchResults(matchedFillResults);
+
+ // Return fill results
+ return matchedFillResults;
+ }
+}
diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/MixinSettlement.sol b/packages/contracts/src/contracts/current/protocol/Exchange/MixinSettlement.sol
index 30f1bb49b..7c03bde75 100644
--- a/packages/contracts/src/contracts/current/protocol/Exchange/MixinSettlement.sol
+++ b/packages/contracts/src/contracts/current/protocol/Exchange/MixinSettlement.sol
@@ -16,15 +16,22 @@
*/
-pragma solidity ^0.4.23;
+pragma solidity ^0.4.24;
+pragma experimental ABIEncoderV2;
import "./mixins/MSettlement.sol";
import "./mixins/MAssetProxyDispatcher.sol";
import "./libs/LibOrder.sol";
import "./libs/LibMath.sol";
+import "./libs/LibExchangeErrors.sol";
+import "./libs/LibFillResults.sol";
+import "./mixins/MMatchOrders.sol";
contract MixinSettlement is
LibMath,
+ LibFillResults,
+ LibExchangeErrors,
+ MMatchOrders,
MSettlement,
MAssetProxyDispatcher
{
@@ -54,46 +61,111 @@ contract MixinSettlement is
/// @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 takerAssetFilledAmount The amount of takerAsset that will be transferred to the order's maker.
- /// @return Amount filled by maker and fees paid by maker/taker.
+ /// @param fillResults Amounts to be filled and fees paid by maker and taker.
function settleOrder(
LibOrder.Order memory order,
address takerAddress,
- uint256 takerAssetFilledAmount)
+ FillResults memory fillResults
+ )
internal
- returns (
- uint256 makerAssetFilledAmount,
- uint256 makerFeePaid,
- uint256 takerFeePaid
- )
{
- makerAssetFilledAmount = getPartialAmount(takerAssetFilledAmount, order.takerAssetAmount, order.makerAssetAmount);
dispatchTransferFrom(
order.makerAssetData,
order.makerAddress,
takerAddress,
- makerAssetFilledAmount
+ fillResults.makerAssetFilledAmount
);
dispatchTransferFrom(
order.takerAssetData,
takerAddress,
order.makerAddress,
- takerAssetFilledAmount
+ fillResults.takerAssetFilledAmount
);
- makerFeePaid = getPartialAmount(takerAssetFilledAmount, order.takerAssetAmount, order.makerFee);
dispatchTransferFrom(
ZRX_PROXY_DATA,
order.makerAddress,
order.feeRecipientAddress,
- makerFeePaid
+ fillResults.makerFeePaid
);
- takerFeePaid = getPartialAmount(takerAssetFilledAmount, order.takerAssetAmount, order.takerFee);
dispatchTransferFrom(
ZRX_PROXY_DATA,
takerAddress,
order.feeRecipientAddress,
- takerFeePaid
+ fillResults.takerFeePaid
);
- return (makerAssetFilledAmount, makerFeePaid, takerFeePaid);
+ }
+
+ /// @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,
+ MatchedFillResults memory matchedFillResults
+ )
+ internal
+ {
+ // 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.takerFillAmount
+ );
+
+ // Maker fees
+ dispatchTransferFrom(
+ ZRX_PROXY_DATA,
+ leftOrder.makerAddress,
+ leftOrder.feeRecipientAddress,
+ matchedFillResults.left.makerFeePaid
+ );
+ dispatchTransferFrom(
+ ZRX_PROXY_DATA,
+ rightOrder.makerAddress,
+ rightOrder.feeRecipientAddress,
+ matchedFillResults.right.makerFeePaid
+ );
+
+ // Taker fees
+ if (leftOrder.feeRecipientAddress == rightOrder.feeRecipientAddress) {
+ dispatchTransferFrom(
+ ZRX_PROXY_DATA,
+ takerAddress,
+ leftOrder.feeRecipientAddress,
+ safeAdd(
+ matchedFillResults.left.takerFeePaid,
+ matchedFillResults.right.takerFeePaid
+ )
+ );
+ } else {
+ dispatchTransferFrom(
+ ZRX_PROXY_DATA,
+ takerAddress,
+ leftOrder.feeRecipientAddress,
+ matchedFillResults.left.takerFeePaid
+ );
+ dispatchTransferFrom(
+ ZRX_PROXY_DATA,
+ takerAddress,
+ rightOrder.feeRecipientAddress,
+ matchedFillResults.right.takerFeePaid
+ );
+ }
}
}
diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/MixinSignatureValidator.sol b/packages/contracts/src/contracts/current/protocol/Exchange/MixinSignatureValidator.sol
index 2322625d9..f7fcd36b6 100644
--- a/packages/contracts/src/contracts/current/protocol/Exchange/MixinSignatureValidator.sol
+++ b/packages/contracts/src/contracts/current/protocol/Exchange/MixinSignatureValidator.sol
@@ -16,7 +16,7 @@
*/
-pragma solidity ^0.4.23;
+pragma solidity ^0.4.24;
import "./mixins/MSignatureValidator.sol";
import "./interfaces/ISigner.sol";
diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/MixinTransactions.sol b/packages/contracts/src/contracts/current/protocol/Exchange/MixinTransactions.sol
index 7f12834a3..f93a80705 100644
--- a/packages/contracts/src/contracts/current/protocol/Exchange/MixinTransactions.sol
+++ b/packages/contracts/src/contracts/current/protocol/Exchange/MixinTransactions.sol
@@ -15,7 +15,7 @@
limitations under the License.
*/
-pragma solidity ^0.4.21;
+pragma solidity ^0.4.24;
import "./mixins/MSignatureValidator.sol";
import "./mixins/MTransactions.sol";
diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/MixinWrapperFunctions.sol b/packages/contracts/src/contracts/current/protocol/Exchange/MixinWrapperFunctions.sol
index 42517221e..15f1a2e0b 100644
--- a/packages/contracts/src/contracts/current/protocol/Exchange/MixinWrapperFunctions.sol
+++ b/packages/contracts/src/contracts/current/protocol/Exchange/MixinWrapperFunctions.sol
@@ -16,7 +16,7 @@
*/
-pragma solidity ^0.4.23;
+pragma solidity ^0.4.24;
pragma experimental ABIEncoderV2;
import "../../utils/LibBytes/LibBytes.sol";
@@ -27,10 +27,11 @@ import "./libs/LibFillResults.sol";
import "./libs/LibExchangeErrors.sol";
contract MixinWrapperFunctions is
+ SafeMath,
+ LibBytes,
+ LibMath,
LibOrder,
LibFillResults,
- LibMath,
- LibBytes,
LibExchangeErrors,
MExchangeCore
{
diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/interfaces/IAssetProxyDispatcher.sol b/packages/contracts/src/contracts/current/protocol/Exchange/interfaces/IAssetProxyDispatcher.sol
index 4e4ed6be8..3ce5ef157 100644
--- a/packages/contracts/src/contracts/current/protocol/Exchange/interfaces/IAssetProxyDispatcher.sol
+++ b/packages/contracts/src/contracts/current/protocol/Exchange/interfaces/IAssetProxyDispatcher.sol
@@ -16,7 +16,7 @@
*/
-pragma solidity ^0.4.23;
+pragma solidity ^0.4.24;
contract IAssetProxyDispatcher {
diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/interfaces/IExchange.sol b/packages/contracts/src/contracts/current/protocol/Exchange/interfaces/IExchange.sol
index 9fba3491b..fc428e9c0 100644
--- a/packages/contracts/src/contracts/current/protocol/Exchange/interfaces/IExchange.sol
+++ b/packages/contracts/src/contracts/current/protocol/Exchange/interfaces/IExchange.sol
@@ -16,19 +16,23 @@
*/
-pragma solidity ^0.4.23;
+pragma solidity ^0.4.24;
pragma experimental ABIEncoderV2;
import "./IExchangeCore.sol";
-import "./ISignatureValidator.sol";
-import "./IAssetProxyDispatcher.sol";
-import "./ITransactions.sol";
-import "./IWrapperFunctions.sol";
+import "./IMatchOrders";
+import "./ISettlement";
+import "./ISignatureValidator";
+import "./ITransactions";
+import "./IAssetProxyDispatcher";
+import "./IWrapperFunctions";
contract IExchange is
- IWrapperFunctions,
IExchangeCore,
+ IMatchOrders,
+ ISettlement,
ISignatureValidator,
ITransactions,
- IAssetProxyDispatcher
+ IAssetProxyDispatcher,
+ IWrapperFunctions
{}
diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/interfaces/IExchangeCore.sol b/packages/contracts/src/contracts/current/protocol/Exchange/interfaces/IExchangeCore.sol
index 0f19525ca..fc0157d75 100644
--- a/packages/contracts/src/contracts/current/protocol/Exchange/interfaces/IExchangeCore.sol
+++ b/packages/contracts/src/contracts/current/protocol/Exchange/interfaces/IExchangeCore.sol
@@ -16,7 +16,7 @@
*/
-pragma solidity ^0.4.23;
+pragma solidity ^0.4.24;
pragma experimental ABIEncoderV2;
import "../libs/LibOrder.sol";
@@ -48,4 +48,33 @@ contract IExchangeCore {
function cancelOrder(LibOrder.Order memory order)
public
returns (bool);
+
+ /// @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(LibOrder.Order memory order)
+ public
+ view
+ returns (LibOrder.OrderInfo memory orderInfo);
+
+ /// @dev Calculates amounts filled and fees paid by maker and taker.
+ /// @param order to be filled.
+ /// @param orderStatus Status of order to be filled.
+ /// @param orderTakerAssetFilledAmount Amount of order already filled.
+ /// @param takerAssetFillAmount Desired amount of order to fill by taker.
+ /// @return status Return status of calculating fill amounts. Returns Status.SUCCESS on success.
+ /// @return fillResults Amounts filled and fees paid by maker and taker.
+ function calculateFillResults(
+ LibOrder.Order memory order,
+ uint8 orderStatus,
+ uint256 orderTakerAssetFilledAmount,
+ uint256 takerAssetFillAmount
+ )
+ public
+ pure
+ returns (
+ uint8 status,
+ LibFillResults.FillResults memory fillResults
+ );
}
diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/interfaces/IMatchOrders.sol b/packages/contracts/src/contracts/current/protocol/Exchange/interfaces/IMatchOrders.sol
new file mode 100644
index 000000000..df009d063
--- /dev/null
+++ b/packages/contracts/src/contracts/current/protocol/Exchange/interfaces/IMatchOrders.sol
@@ -0,0 +1,44 @@
+/*
+
+ Copyright 2018 ZeroEx Intl.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+*/
+pragma solidity ^0.4.24;
+pragma experimental ABIEncoderV2;
+
+import "../libs/LibOrder.sol";
+import "../libs/LibFillResults.sol";
+
+contract IMatchOrders {
+
+ /// @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.
+ /// TODO: Make this function external once supported by Solidity (See Solidity Issues #3199, #1603)
+ function matchOrders(
+ LibOrder.Order memory leftOrder,
+ LibOrder.Order memory rightOrder,
+ bytes memory leftSignature,
+ bytes memory rightSignature
+ )
+ public
+ returns (LibFillResults.MatchedFillResults memory matchedFillResults);
+}
diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/interfaces/ISignatureValidator.sol b/packages/contracts/src/contracts/current/protocol/Exchange/interfaces/ISignatureValidator.sol
index b4a238472..65ff45f7b 100644
--- a/packages/contracts/src/contracts/current/protocol/Exchange/interfaces/ISignatureValidator.sol
+++ b/packages/contracts/src/contracts/current/protocol/Exchange/interfaces/ISignatureValidator.sol
@@ -16,7 +16,7 @@
*/
-pragma solidity ^0.4.23;
+pragma solidity ^0.4.24;
contract ISignatureValidator {
diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/interfaces/ISigner.sol b/packages/contracts/src/contracts/current/protocol/Exchange/interfaces/ISigner.sol
index e065cfd8a..53c41d331 100644
--- a/packages/contracts/src/contracts/current/protocol/Exchange/interfaces/ISigner.sol
+++ b/packages/contracts/src/contracts/current/protocol/Exchange/interfaces/ISigner.sol
@@ -16,7 +16,7 @@
*/
-pragma solidity ^0.4.23;
+pragma solidity ^0.4.24;
contract ISigner {
diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/interfaces/ITransactions.sol b/packages/contracts/src/contracts/current/protocol/Exchange/interfaces/ITransactions.sol
index 76e4cf2fe..d973bf001 100644
--- a/packages/contracts/src/contracts/current/protocol/Exchange/interfaces/ITransactions.sol
+++ b/packages/contracts/src/contracts/current/protocol/Exchange/interfaces/ITransactions.sol
@@ -15,7 +15,7 @@
limitations under the License.
*/
-pragma solidity ^0.4.23;
+pragma solidity ^0.4.24;
contract ITransactions {
diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/interfaces/IWrapperFunctions.sol b/packages/contracts/src/contracts/current/protocol/Exchange/interfaces/IWrapperFunctions.sol
index d23170b1c..1eb1233ed 100644
--- a/packages/contracts/src/contracts/current/protocol/Exchange/interfaces/IWrapperFunctions.sol
+++ b/packages/contracts/src/contracts/current/protocol/Exchange/interfaces/IWrapperFunctions.sol
@@ -16,17 +16,17 @@
*/
-pragma solidity ^0.4.23;
+pragma solidity ^0.4.24;
pragma experimental ABIEncoderV2;
import "./libs/LibOrder.sol";
import "./libs/LibFillResults.sol";
contract IWrapperFunctions is
+ LibBytes,
+ LibMath,
LibOrder,
LibFillResults,
- LibMath,
- LibBytes,
LibExchangeErrors,
MExchangeCore
{
diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/libs/LibExchangeErrors.sol b/packages/contracts/src/contracts/current/protocol/Exchange/libs/LibExchangeErrors.sol
index 6ffbdfdca..4712ee36c 100644
--- a/packages/contracts/src/contracts/current/protocol/Exchange/libs/LibExchangeErrors.sol
+++ b/packages/contracts/src/contracts/current/protocol/Exchange/libs/LibExchangeErrors.sol
@@ -16,21 +16,10 @@
*/
-pragma solidity ^0.4.23;
+pragma solidity ^0.4.24;
contract LibExchangeErrors {
- // Error Codes
- enum Errors {
- ORDER_EXPIRED, // Order has already expired
- ORDER_FULLY_FILLED, // Order has already been fully filled
- ORDER_CANCELLED, // Order has already been cancelled
- ROUNDING_ERROR_TOO_LARGE, // Rounding error too large
- INSUFFICIENT_BALANCE_OR_ALLOWANCE // Insufficient balance or allowance for token transfer
- }
-
- event ExchangeError(uint8 indexed errorId, bytes32 indexed orderHash);
-
// Core revert reasons
string constant GT_ZERO_AMOUNT_REQUIRED = "Amount must be greater than 0.";
string constant SIGNATURE_VALIDATION_FAILED = "Signature validation failed.";
@@ -38,6 +27,10 @@ contract LibExchangeErrors {
string constant INVALID_CONTEXT = "Function called in an invalid context.";
string constant INVALID_NEW_MAKER_EPOCH = "Specified salt must be greater than or equal to existing makerEpoch.";
+ // Order revert reasons
+ string constant INVALID_ORDER_TAKER_ASSET_AMOUNT = "Invalid order taker asset amount: expected a non-zero value.";
+ string constant INVALID_ORDER_MAKER_ASSET_AMOUNT = "Invalid order maker asset amount: expected a non-zero value.";
+
// Transaction revert reasons
string constant DUPLICATE_TRANSACTION_HASH = "Transaction has already been executed.";
string constant TRANSACTION_EXECUTION_FAILED = "Transaction execution failed.";
@@ -55,4 +48,13 @@ contract LibExchangeErrors {
string constant INVALID_SIGNATURE_LENGTH = "Invalid signature length.";
string constant ILLEGAL_SIGNATURE_TYPE = "Illegal signature type.";
string constant UNSUPPORTED_SIGNATURE_TYPE = "Unsupported signature type.";
+
+ // Order matching revert reasons
+ string constant ASSET_MISMATCH_MAKER_TAKER = "Left order maker asset is different from right order taker asset.";
+ string constant ASSET_MISMATCH_TAKER_MAKER = "Left order taker asset is different from right order maker asset.";
+ string constant NEGATIVE_SPREAD = "Matched orders must have a positive spread.";
+ string constant MISCALCULATED_TRANSFER_AMOUNTS = "A miscalculation occurred: the left maker would receive more than the right maker would spend.";
+ string constant ROUNDING_ERROR_TRANSFER_AMOUNTS = "A rounding error occurred when calculating transfer amounts for matched orders.";
+ string constant FAILED_TO_CALCULATE_FILL_RESULTS_FOR_LEFT_ORDER = "Failed to calculate fill results for left order.";
+ string constant FAILED_TO_CALCULATE_FILL_RESULTS_FOR_RIGHT_ORDER = "Failed to calculate fill results for right order.";
}
diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/libs/LibFillResults.sol b/packages/contracts/src/contracts/current/protocol/Exchange/libs/LibFillResults.sol
index 3b8f2acf9..aa54598fa 100644
--- a/packages/contracts/src/contracts/current/protocol/Exchange/libs/LibFillResults.sol
+++ b/packages/contracts/src/contracts/current/protocol/Exchange/libs/LibFillResults.sol
@@ -16,7 +16,7 @@
*/
-pragma solidity ^0.4.23;
+pragma solidity ^0.4.24;
import "../../../utils/SafeMath/SafeMath.sol";
@@ -31,6 +31,12 @@ contract LibFillResults is
uint256 takerFeePaid;
}
+ struct MatchedFillResults {
+ LibFillResults.FillResults left;
+ LibFillResults.FillResults right;
+ uint256 takerFillAmount;
+ }
+
/// @dev Adds properties of both FillResults instances.
/// Modifies the first FillResults instance specified.
/// @param totalFillResults Fill results instance that will be added onto.
@@ -44,4 +50,19 @@ contract LibFillResults is
totalFillResults.makerFeePaid = safeAdd(totalFillResults.makerFeePaid, singleFillResults.makerFeePaid);
totalFillResults.takerFeePaid = safeAdd(totalFillResults.takerFeePaid, singleFillResults.takerFeePaid);
}
-} \ No newline at end of file
+
+ /// @dev Returns a null fill results struct
+ function getNullFillResults()
+ internal
+ pure
+ returns (FillResults memory)
+ {
+ // returns zeroed out FillResults instance
+ return FillResults({
+ makerAssetFilledAmount: 0,
+ takerAssetFilledAmount: 0,
+ makerFeePaid: 0,
+ takerFeePaid: 0
+ });
+ }
+}
diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/libs/LibMath.sol b/packages/contracts/src/contracts/current/protocol/Exchange/libs/LibMath.sol
index b48602d19..ea8c138d6 100644
--- a/packages/contracts/src/contracts/current/protocol/Exchange/libs/LibMath.sol
+++ b/packages/contracts/src/contracts/current/protocol/Exchange/libs/LibMath.sol
@@ -16,13 +16,14 @@
*/
-pragma solidity ^0.4.23;
+pragma solidity ^0.4.24;
import "../../../utils/SafeMath/SafeMath.sol";
contract LibMath is
SafeMath
{
+ string constant ROUNDING_ERROR_ON_PARTIAL_AMOUNT = "A rounding error occurred when calculating partial transfer amounts.";
/// @dev Calculates partial value given a numerator and denominator.
/// @param numerator Numerator.
@@ -44,6 +45,26 @@ contract LibMath is
return partialAmount;
}
+ /// @dev Calculates partial value given a numerator and denominator.
+ /// Throws if there is a rounding error.
+ /// @param numerator Numerator.
+ /// @param denominator Denominator.
+ /// @param target Value to calculate partial of.
+ /// @return Partial value of target.
+ function safeGetPartialAmount(
+ uint256 numerator,
+ uint256 denominator,
+ uint256 target)
+ internal pure
+ returns (uint256 partialAmount)
+ {
+ require(
+ !isRoundingError(numerator, denominator, target),
+ ROUNDING_ERROR_ON_PARTIAL_AMOUNT
+ );
+ return getPartialAmount(numerator, denominator, target);
+ }
+
/// @dev Checks if rounding error > 0.1%.
/// @param numerator Numerator.
/// @param denominator Denominator.
diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/libs/LibOrder.sol b/packages/contracts/src/contracts/current/protocol/Exchange/libs/LibOrder.sol
index 0fe8c8c96..7d8328c67 100644
--- a/packages/contracts/src/contracts/current/protocol/Exchange/libs/LibOrder.sol
+++ b/packages/contracts/src/contracts/current/protocol/Exchange/libs/LibOrder.sol
@@ -16,7 +16,7 @@
*/
-pragma solidity ^0.4.23;
+pragma solidity ^0.4.24;
contract LibOrder {
@@ -51,6 +51,15 @@ contract LibOrder {
bytes takerAssetData;
}
+ struct OrderInfo {
+ // See LibStatus for a complete description of order statuses
+ uint8 orderStatus;
+ // Keccak-256 EIP712 hash of the order
+ bytes32 orderHash;
+ // Amount of order that has been filled
+ uint256 orderTakerAssetFilledAmount;
+ }
+
/// @dev Calculates Keccak-256 hash of the order.
/// @param order The order structure.
/// @return Keccak-256 EIP712 hash of the order.
diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/libs/LibStatus.sol b/packages/contracts/src/contracts/current/protocol/Exchange/libs/LibStatus.sol
new file mode 100644
index 000000000..f72b7d65f
--- /dev/null
+++ b/packages/contracts/src/contracts/current/protocol/Exchange/libs/LibStatus.sol
@@ -0,0 +1,51 @@
+/*
+
+ 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;
+
+contract LibStatus {
+
+ // Exchange Status Codes
+ enum Status {
+ /// Default Status ///
+ INVALID, // General invalid status
+
+ /// General Exchange Statuses ///
+ SUCCESS, // Indicates a successful operation
+ ROUNDING_ERROR_TOO_LARGE, // Rounding error too large
+ INSUFFICIENT_BALANCE_OR_ALLOWANCE, // Insufficient balance or allowance for token transfer
+ TAKER_ASSET_FILL_AMOUNT_TOO_LOW, // takerAssetFillAmount is <= 0
+ INVALID_SIGNATURE, // Invalid signature
+ INVALID_SENDER, // Invalid sender
+ INVALID_TAKER, // Invalid taker
+ INVALID_MAKER, // Invalid maker
+
+ /// Order State Statuses ///
+ // A valid order remains fillable until it is expired, fully filled, or cancelled.
+ // An order's state is unaffected by external factors, like account balances.
+ ORDER_INVALID_MAKER_ASSET_AMOUNT, // Order does not have a valid maker asset amount
+ ORDER_INVALID_TAKER_ASSET_AMOUNT, // Order does not have a valid taker asset amount
+ ORDER_FILLABLE, // Order is fillable
+ ORDER_EXPIRED, // Order has already expired
+ ORDER_FULLY_FILLED, // Order is fully filled
+ ORDER_CANCELLED // Order has been cancelled
+ }
+
+ event ExchangeStatus(uint8 indexed statusId, bytes32 indexed orderHash);
+}
diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/mixins/MAssetProxyDispatcher.sol b/packages/contracts/src/contracts/current/protocol/Exchange/mixins/MAssetProxyDispatcher.sol
index 77ee77797..ccc960d6e 100644
--- a/packages/contracts/src/contracts/current/protocol/Exchange/mixins/MAssetProxyDispatcher.sol
+++ b/packages/contracts/src/contracts/current/protocol/Exchange/mixins/MAssetProxyDispatcher.sol
@@ -16,7 +16,8 @@
*/
-pragma solidity ^0.4.23;
+pragma solidity ^0.4.24;
+pragma experimental ABIEncoderV2;
import "../interfaces/IAssetProxyDispatcher.sol";
diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/mixins/MExchangeCore.sol b/packages/contracts/src/contracts/current/protocol/Exchange/mixins/MExchangeCore.sol
index 5b78e70da..ae1e50637 100644
--- a/packages/contracts/src/contracts/current/protocol/Exchange/mixins/MExchangeCore.sol
+++ b/packages/contracts/src/contracts/current/protocol/Exchange/mixins/MExchangeCore.sol
@@ -16,7 +16,8 @@
*/
-pragma solidity ^0.4.23;
+pragma solidity ^0.4.24;
+pragma experimental ABIEncoderV2;
import "../libs/LibOrder.sol";
import "../libs/LibFillResults.sol";
@@ -55,12 +56,62 @@ contract MExchangeCore is
uint256 makerEpoch
);
- /// @dev Logs a Fill event with the given arguments.
- /// The sole purpose of this function is to get around the stack variable limit.
- function emitFillEvent(
+ /// @dev Validates context for fillOrder. Succeeds or throws.
+ /// @param order to be filled.
+ /// @param orderStatus Status of order to be filled.
+ /// @param orderHash Hash of order to be filled.
+ /// @param takerAddress Address of order taker.
+ /// @param orderTakerAssetFilledAmount Amount of order already filled.
+ /// @param takerAssetFillAmount Desired amount of order to fill by taker.
+ /// @param signature Proof that the orders was created by its maker.
+ function assertValidFill(
+ LibOrder.Order memory order,
+ uint8 orderStatus,
+ bytes32 orderHash,
+ address takerAddress,
+ uint256 orderTakerAssetFilledAmount,
+ uint256 takerAssetFillAmount,
+ bytes memory signature
+ )
+ 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,
- LibFillResults.FillResults memory fillResults)
+ uint256 orderTakerAssetFilledAmount,
+ LibFillResults.FillResults memory fillResults
+ )
internal;
+
+ /// @dev Validates context for cancelOrder. Succeeds or throws.
+ /// @param order that was cancelled.
+ /// @param orderStatus Status of order that was cancelled.
+ /// @param orderHash Hash of order that was cancelled.
+ function assertValidCancel(
+ LibOrder.Order memory order,
+ uint8 orderStatus,
+ bytes32 orderHash
+ )
+ 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 orderStatus Status of order that was cancelled.
+ /// @param orderHash Hash of order that was cancelled.
+ /// @return stateUpdated Returns true only if state was updated.
+ function updateCancelledState(
+ LibOrder.Order memory order,
+ uint8 orderStatus,
+ bytes32 orderHash
+ )
+ internal
+ returns (bool stateUpdated);
}
diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/mixins/MMatchOrders.sol b/packages/contracts/src/contracts/current/protocol/Exchange/mixins/MMatchOrders.sol
new file mode 100644
index 000000000..7dd608cf2
--- /dev/null
+++ b/packages/contracts/src/contracts/current/protocol/Exchange/mixins/MMatchOrders.sol
@@ -0,0 +1,65 @@
+/*
+
+ Copyright 2018 ZeroEx Intl.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+*/
+pragma solidity ^0.4.24;
+pragma experimental ABIEncoderV2;
+
+import "../libs/LibOrder.sol";
+import "../libs/LibFillResults.sol";
+import "./MExchangeCore.sol";
+import "../interfaces/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;
+
+ /// @dev Validates matched fill results. Succeeds or throws.
+ /// @param matchedFillResults Amounts to fill and fees to pay by maker and taker of matched orders.
+ function assertValidMatchResults(LibFillResults.MatchedFillResults memory matchedFillResults)
+ internal;
+
+ /// @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 leftOrderStatus Order status of left order.
+ /// @param rightOrderStatus Order status of right order.
+ /// @param leftOrderFilledAmount Amount of left order already filled.
+ /// @param rightOrderFilledAmount 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,
+ uint8 leftOrderStatus,
+ uint8 rightOrderStatus,
+ uint256 leftOrderFilledAmount,
+ uint256 rightOrderFilledAmount
+ )
+ internal
+ returns (LibFillResults.MatchedFillResults memory matchedFillResults);
+}
diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/mixins/MSettlement.sol b/packages/contracts/src/contracts/current/protocol/Exchange/mixins/MSettlement.sol
index 5e2edbf99..50b62e79f 100644
--- a/packages/contracts/src/contracts/current/protocol/Exchange/mixins/MSettlement.sol
+++ b/packages/contracts/src/contracts/current/protocol/Exchange/mixins/MSettlement.sol
@@ -16,26 +16,35 @@
*/
-pragma solidity ^0.4.23;
+pragma solidity ^0.4.24;
import "../libs/LibOrder.sol";
+import "./MMatchOrders.sol";
+import "../libs/LibFillResults.sol";
contract MSettlement {
- /// @dev Settles an order by transfering assets between counterparties.
+ /// @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 takerAssetFilledAmount The amount of takerAsset that will be transfered to the order's maker.
- /// @return Amount filled by maker and fees paid by maker/taker.
+ /// @param fillResults Amounts to be filled and fees paid by maker and taker.
function settleOrder(
LibOrder.Order memory order,
address takerAddress,
- uint256 takerAssetFilledAmount)
- internal
- returns (
- uint256 makerAssetFilledAmount,
- uint256 makerFeePaid,
- uint256 takerFeePaid
- );
+ LibFillResults.FillResults memory fillResults
+ )
+ internal;
+
+ /// @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
+ )
+ internal;
}
-
diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/mixins/MSignatureValidator.sol b/packages/contracts/src/contracts/current/protocol/Exchange/mixins/MSignatureValidator.sol
index ba26c07f6..3658e7c6f 100644
--- a/packages/contracts/src/contracts/current/protocol/Exchange/mixins/MSignatureValidator.sol
+++ b/packages/contracts/src/contracts/current/protocol/Exchange/mixins/MSignatureValidator.sol
@@ -16,7 +16,7 @@
*/
-pragma solidity ^0.4.23;
+pragma solidity ^0.4.24;
import "../interfaces/ISignatureValidator.sol";
diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/mixins/MTransactions.sol b/packages/contracts/src/contracts/current/protocol/Exchange/mixins/MTransactions.sol
index 159ed1527..e2f89de01 100644
--- a/packages/contracts/src/contracts/current/protocol/Exchange/mixins/MTransactions.sol
+++ b/packages/contracts/src/contracts/current/protocol/Exchange/mixins/MTransactions.sol
@@ -15,7 +15,7 @@
limitations under the License.
*/
-pragma solidity ^0.4.23;
+pragma solidity ^0.4.24;
import "../interfaces/ITransactions.sol";
diff --git a/packages/contracts/src/contracts/current/test/DummyERC20Token/DummyERC20Token.sol b/packages/contracts/src/contracts/current/test/DummyERC20Token/DummyERC20Token.sol
index ab5311e0c..0c7b18c0c 100644
--- a/packages/contracts/src/contracts/current/test/DummyERC20Token/DummyERC20Token.sol
+++ b/packages/contracts/src/contracts/current/test/DummyERC20Token/DummyERC20Token.sol
@@ -16,7 +16,7 @@
*/
-pragma solidity ^0.4.23;
+pragma solidity ^0.4.24;
pragma experimental ABIEncoderV2;
import "../Mintable/Mintable.sol";
diff --git a/packages/contracts/src/contracts/current/test/DummyERC721Token/DummyERC721Token.sol b/packages/contracts/src/contracts/current/test/DummyERC721Token/DummyERC721Token.sol
index 22ebbd3c1..369a2950d 100644
--- a/packages/contracts/src/contracts/current/test/DummyERC721Token/DummyERC721Token.sol
+++ b/packages/contracts/src/contracts/current/test/DummyERC721Token/DummyERC721Token.sol
@@ -16,7 +16,7 @@
*/
-pragma solidity ^0.4.23;
+pragma solidity ^0.4.24;
pragma experimental ABIEncoderV2;
import "../../tokens/ERC721Token/ERC721Token.sol";
diff --git a/packages/contracts/src/contracts/current/test/Mintable/Mintable.sol b/packages/contracts/src/contracts/current/test/Mintable/Mintable.sol
index fd944f244..a91bfee9e 100644
--- a/packages/contracts/src/contracts/current/test/Mintable/Mintable.sol
+++ b/packages/contracts/src/contracts/current/test/Mintable/Mintable.sol
@@ -16,7 +16,7 @@
*/
-pragma solidity ^0.4.23;
+pragma solidity ^0.4.24;
pragma experimental ABIEncoderV2;
import "../../tokens/UnlimitedAllowanceToken/UnlimitedAllowanceToken.sol";
diff --git a/packages/contracts/src/contracts/current/test/TestAssetProxyDispatcher/TestAssetProxyDispatcher.sol b/packages/contracts/src/contracts/current/test/TestAssetProxyDispatcher/TestAssetProxyDispatcher.sol
index 93ec3cef3..11ca0617d 100644
--- a/packages/contracts/src/contracts/current/test/TestAssetProxyDispatcher/TestAssetProxyDispatcher.sol
+++ b/packages/contracts/src/contracts/current/test/TestAssetProxyDispatcher/TestAssetProxyDispatcher.sol
@@ -16,7 +16,7 @@
*/
-pragma solidity ^0.4.23;
+pragma solidity ^0.4.24;
pragma experimental ABIEncoderV2;
import "../../protocol/Exchange/MixinAssetProxyDispatcher.sol";
diff --git a/packages/contracts/src/contracts/current/test/TestLibBytes/TestLibBytes.sol b/packages/contracts/src/contracts/current/test/TestLibBytes/TestLibBytes.sol
index 1597ff8d5..ac4602933 100644
--- a/packages/contracts/src/contracts/current/test/TestLibBytes/TestLibBytes.sol
+++ b/packages/contracts/src/contracts/current/test/TestLibBytes/TestLibBytes.sol
@@ -16,7 +16,7 @@
*/
-pragma solidity ^0.4.23;
+pragma solidity ^0.4.24;
pragma experimental ABIEncoderV2;
import "../../utils/LibBytes/LibBytes.sol";
diff --git a/packages/contracts/src/contracts/current/test/TestLibs/TestLibs.sol b/packages/contracts/src/contracts/current/test/TestLibs/TestLibs.sol
index 0dc7785b2..b8fc90af1 100644
--- a/packages/contracts/src/contracts/current/test/TestLibs/TestLibs.sol
+++ b/packages/contracts/src/contracts/current/test/TestLibs/TestLibs.sol
@@ -16,7 +16,7 @@
*/
-pragma solidity ^0.4.23;
+pragma solidity ^0.4.24;
pragma experimental ABIEncoderV2;
import "../../protocol/Exchange/libs/LibMath.sol";
@@ -77,4 +77,4 @@ contract TestLibs is
addFillResults(totalFillResults, singleFillResults);
return totalFillResults;
}
-} \ No newline at end of file
+}
diff --git a/packages/contracts/src/contracts/current/test/TestSignatureValidator/TestSignatureValidator.sol b/packages/contracts/src/contracts/current/test/TestSignatureValidator/TestSignatureValidator.sol
index 20202cd7b..15d9ca189 100644
--- a/packages/contracts/src/contracts/current/test/TestSignatureValidator/TestSignatureValidator.sol
+++ b/packages/contracts/src/contracts/current/test/TestSignatureValidator/TestSignatureValidator.sol
@@ -16,7 +16,7 @@
*/
-pragma solidity ^0.4.23;
+pragma solidity ^0.4.24;
pragma experimental ABIEncoderV2;
import "../../protocol/Exchange/MixinSignatureValidator.sol";
@@ -38,4 +38,4 @@ contract TestSignatureValidator is MixinSignatureValidator {
);
return isValid;
}
-} \ No newline at end of file
+}
diff --git a/packages/contracts/src/contracts/current/tokens/ERC20Token/ERC20Token.sol b/packages/contracts/src/contracts/current/tokens/ERC20Token/ERC20Token.sol
index 6497d3c7a..f0bcdafef 100644
--- a/packages/contracts/src/contracts/current/tokens/ERC20Token/ERC20Token.sol
+++ b/packages/contracts/src/contracts/current/tokens/ERC20Token/ERC20Token.sol
@@ -16,7 +16,7 @@
*/
-pragma solidity ^0.4.23;
+pragma solidity ^0.4.24;
pragma experimental ABIEncoderV2;
import "./IERC20Token.sol";
diff --git a/packages/contracts/src/contracts/current/tokens/ERC20Token/IERC20Token.sol b/packages/contracts/src/contracts/current/tokens/ERC20Token/IERC20Token.sol
index 537f5a83d..eb879b6a8 100644
--- a/packages/contracts/src/contracts/current/tokens/ERC20Token/IERC20Token.sol
+++ b/packages/contracts/src/contracts/current/tokens/ERC20Token/IERC20Token.sol
@@ -16,7 +16,7 @@
*/
-pragma solidity ^0.4.23;
+pragma solidity ^0.4.24;
pragma experimental ABIEncoderV2;
contract IERC20Token {
diff --git a/packages/contracts/src/contracts/current/tokens/ERC721Token/ERC721Token.sol b/packages/contracts/src/contracts/current/tokens/ERC721Token/ERC721Token.sol
index b3493bc99..41ba149e3 100644
--- a/packages/contracts/src/contracts/current/tokens/ERC721Token/ERC721Token.sol
+++ b/packages/contracts/src/contracts/current/tokens/ERC721Token/ERC721Token.sol
@@ -23,7 +23,7 @@ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
-pragma solidity ^0.4.23;
+pragma solidity ^0.4.24;
import "./IERC721Token.sol";
import "./IERC721Receiver.sol";
@@ -403,4 +403,4 @@ contract ERC721Token is
assembly { size := extcodesize(addr) } // solium-disable-line security/no-inline-assembly
return size > 0;
}
-} \ No newline at end of file
+}
diff --git a/packages/contracts/src/contracts/current/tokens/ERC721Token/IERC721Receiver.sol b/packages/contracts/src/contracts/current/tokens/ERC721Token/IERC721Receiver.sol
index 3484bf824..b0fff3c90 100644
--- a/packages/contracts/src/contracts/current/tokens/ERC721Token/IERC721Receiver.sol
+++ b/packages/contracts/src/contracts/current/tokens/ERC721Token/IERC721Receiver.sol
@@ -23,7 +23,7 @@ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
-pragma solidity ^0.4.23;
+pragma solidity ^0.4.24;
/**
* @title ERC721 token receiver interface
@@ -57,4 +57,4 @@ contract IERC721Receiver {
bytes _data)
public
returns (bytes4);
-} \ No newline at end of file
+}
diff --git a/packages/contracts/src/contracts/current/tokens/ERC721Token/IERC721Token.sol b/packages/contracts/src/contracts/current/tokens/ERC721Token/IERC721Token.sol
index 81e1b97af..345712d67 100644
--- a/packages/contracts/src/contracts/current/tokens/ERC721Token/IERC721Token.sol
+++ b/packages/contracts/src/contracts/current/tokens/ERC721Token/IERC721Token.sol
@@ -23,7 +23,7 @@ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
-pragma solidity ^0.4.23;
+pragma solidity ^0.4.24;
/**
* @title ERC721 Non-Fungible Token Standard basic interface
@@ -102,4 +102,4 @@ contract IERC721Token {
uint256 _tokenId,
bytes _data)
public;
-} \ No newline at end of file
+}
diff --git a/packages/contracts/src/contracts/current/tokens/UnlimitedAllowanceToken/UnlimitedAllowanceToken.sol b/packages/contracts/src/contracts/current/tokens/UnlimitedAllowanceToken/UnlimitedAllowanceToken.sol
index 395e5d356..f62602ab3 100644
--- a/packages/contracts/src/contracts/current/tokens/UnlimitedAllowanceToken/UnlimitedAllowanceToken.sol
+++ b/packages/contracts/src/contracts/current/tokens/UnlimitedAllowanceToken/UnlimitedAllowanceToken.sol
@@ -16,7 +16,7 @@
*/
-pragma solidity ^0.4.23;
+pragma solidity ^0.4.24;
pragma experimental ABIEncoderV2;
import "../ERC20Token/ERC20Token.sol";
diff --git a/packages/contracts/src/contracts/current/utils/LibBytes/LibBytes.sol b/packages/contracts/src/contracts/current/utils/LibBytes/LibBytes.sol
index 3c5531e35..2c5d9e756 100644
--- a/packages/contracts/src/contracts/current/utils/LibBytes/LibBytes.sol
+++ b/packages/contracts/src/contracts/current/utils/LibBytes/LibBytes.sol
@@ -16,7 +16,7 @@
*/
-pragma solidity ^0.4.23;
+pragma solidity ^0.4.24;
contract LibBytes {
diff --git a/packages/contracts/src/contracts/current/utils/Ownable/IOwnable.sol b/packages/contracts/src/contracts/current/utils/Ownable/IOwnable.sol
index 63b04945f..e77680903 100644
--- a/packages/contracts/src/contracts/current/utils/Ownable/IOwnable.sol
+++ b/packages/contracts/src/contracts/current/utils/Ownable/IOwnable.sol
@@ -1,4 +1,4 @@
-pragma solidity ^0.4.23;
+pragma solidity ^0.4.24;
pragma experimental ABIEncoderV2;
/*
diff --git a/packages/contracts/src/contracts/current/utils/Ownable/Ownable.sol b/packages/contracts/src/contracts/current/utils/Ownable/Ownable.sol
index 933aa168a..296c6c856 100644
--- a/packages/contracts/src/contracts/current/utils/Ownable/Ownable.sol
+++ b/packages/contracts/src/contracts/current/utils/Ownable/Ownable.sol
@@ -1,4 +1,4 @@
-pragma solidity ^0.4.23;
+pragma solidity ^0.4.24;
pragma experimental ABIEncoderV2;
/*
diff --git a/packages/contracts/src/contracts/current/utils/SafeMath/SafeMath.sol b/packages/contracts/src/contracts/current/utils/SafeMath/SafeMath.sol
index 1ab27eebc..e137f6ca5 100644
--- a/packages/contracts/src/contracts/current/utils/SafeMath/SafeMath.sol
+++ b/packages/contracts/src/contracts/current/utils/SafeMath/SafeMath.sol
@@ -1,4 +1,4 @@
-pragma solidity ^0.4.23;
+pragma solidity ^0.4.24;
pragma experimental ABIEncoderV2;
contract SafeMath {
diff --git a/packages/contracts/src/utils/address_utils.ts b/packages/contracts/src/utils/address_utils.ts
index 01a7a6fd4..dc63459f9 100644
--- a/packages/contracts/src/utils/address_utils.ts
+++ b/packages/contracts/src/utils/address_utils.ts
@@ -1,10 +1,10 @@
-import { ZeroEx } from '0x.js';
+import { generatePseudoRandomSalt } from '@0xproject/order-utils';
import { crypto } from './crypto';
export const addressUtils = {
generatePseudoRandomAddress(): string {
- const randomBigNum = ZeroEx.generatePseudoRandomSalt();
+ const randomBigNum = generatePseudoRandomSalt();
const randomBuff = crypto.solSHA3([randomBigNum]);
const randomAddress = `0x${randomBuff.slice(0, 20).toString('hex')}`;
return randomAddress;
diff --git a/packages/contracts/src/utils/asset_proxy_utils.ts b/packages/contracts/src/utils/asset_proxy_utils.ts
index dc31c3497..c042da5d0 100644
--- a/packages/contracts/src/utils/asset_proxy_utils.ts
+++ b/packages/contracts/src/utils/asset_proxy_utils.ts
@@ -2,12 +2,15 @@ import { BigNumber } from '@0xproject/utils';
import BN = require('bn.js');
import ethUtil = require('ethereumjs-util');
-import { AssetProxyId } from './types';
+import { AssetProxyId, ERC20ProxyData, ERC721ProxyData, ProxyData } from './types';
export const assetProxyUtils = {
encodeAssetProxyId(assetProxyId: AssetProxyId): Buffer {
return ethUtil.toBuffer(assetProxyId);
},
+ decodeAssetProxyId(encodedAssetProxyId: Buffer): AssetProxyId {
+ return ethUtil.bufferToInt(encodedAssetProxyId);
+ },
encodeAddress(address: string): Buffer {
if (!ethUtil.isValidAddress(address)) {
throw new Error(`Invalid Address: ${address}`);
@@ -15,12 +18,24 @@ export const assetProxyUtils = {
const encodedAddress = ethUtil.toBuffer(address);
return encodedAddress;
},
+ decodeAddress(encodedAddress: Buffer): string {
+ const address = ethUtil.bufferToHex(encodedAddress);
+ if (!ethUtil.isValidAddress(address)) {
+ throw new Error(`Invalid Address: ${address}`);
+ }
+ return address;
+ },
encodeUint256(value: BigNumber): Buffer {
const formattedValue = new BN(value.toString(10));
const encodedValue = ethUtil.toBuffer(formattedValue);
const paddedValue = ethUtil.setLengthLeft(encodedValue, 32);
return paddedValue;
},
+ decodeUint256(encodedValue: Buffer): BigNumber {
+ const formattedValue = ethUtil.bufferToHex(encodedValue);
+ const value = new BigNumber(formattedValue, 16);
+ return value;
+ },
encodeERC20ProxyData(tokenAddress: string): string {
const encodedAssetProxyId = assetProxyUtils.encodeAssetProxyId(AssetProxyId.ERC20);
const encodedAddress = assetProxyUtils.encodeAddress(tokenAddress);
@@ -28,6 +43,32 @@ export const assetProxyUtils = {
const encodedMetadataHex = ethUtil.bufferToHex(encodedMetadata);
return encodedMetadataHex;
},
+ decodeERC20ProxyData(proxyData: string): ERC20ProxyData {
+ const encodedProxyMetadata = ethUtil.toBuffer(proxyData);
+ if (encodedProxyMetadata.byteLength !== 21) {
+ throw new Error(
+ `Could not decode ERC20 Proxy Data. Expected length of encoded data to be 21. Got ${
+ encodedProxyMetadata.byteLength
+ }`,
+ );
+ }
+ const encodedAssetProxyId = encodedProxyMetadata.slice(0, 1);
+ const assetProxyId = assetProxyUtils.decodeAssetProxyId(encodedAssetProxyId);
+ if (assetProxyId !== AssetProxyId.ERC20) {
+ throw new Error(
+ `Could not decode ERC20 Proxy Data. Expected Asset Proxy Id to be ERC20 (${
+ AssetProxyId.ERC20
+ }), but got ${assetProxyId}`,
+ );
+ }
+ const encodedTokenAddress = encodedProxyMetadata.slice(1, 21);
+ const tokenAddress = assetProxyUtils.decodeAddress(encodedTokenAddress);
+ const erc20ProxyData = {
+ assetProxyId,
+ tokenAddress,
+ };
+ return erc20ProxyData;
+ },
encodeERC721ProxyData(tokenAddress: string, tokenId: BigNumber): string {
const encodedAssetProxyId = assetProxyUtils.encodeAssetProxyId(AssetProxyId.ERC721);
const encodedAddress = assetProxyUtils.encodeAddress(tokenAddress);
@@ -36,4 +77,68 @@ export const assetProxyUtils = {
const encodedMetadataHex = ethUtil.bufferToHex(encodedMetadata);
return encodedMetadataHex;
},
+ decodeERC721ProxyData(proxyData: string): ERC721ProxyData {
+ const encodedProxyMetadata = ethUtil.toBuffer(proxyData);
+ if (encodedProxyMetadata.byteLength !== 53) {
+ throw new Error(
+ `Could not decode ERC20 Proxy Data. Expected length of encoded data to be 53. Got ${
+ encodedProxyMetadata.byteLength
+ }`,
+ );
+ }
+ const encodedAssetProxyId = encodedProxyMetadata.slice(0, 1);
+ const assetProxyId = assetProxyUtils.decodeAssetProxyId(encodedAssetProxyId);
+ if (assetProxyId !== AssetProxyId.ERC721) {
+ throw new Error(
+ `Could not decode ERC721 Proxy Data. Expected Asset Proxy Id to be ERC721 (${
+ AssetProxyId.ERC721
+ }), but got ${assetProxyId}`,
+ );
+ }
+ const encodedTokenAddress = encodedProxyMetadata.slice(1, 21);
+ const tokenAddress = assetProxyUtils.decodeAddress(encodedTokenAddress);
+ const encodedTokenId = encodedProxyMetadata.slice(21, 53);
+ const tokenId = assetProxyUtils.decodeUint256(encodedTokenId);
+ const erc721ProxyData = {
+ assetProxyId,
+ tokenAddress,
+ tokenId,
+ };
+ return erc721ProxyData;
+ },
+ decodeProxyDataId(proxyData: string): AssetProxyId {
+ const encodedProxyMetadata = ethUtil.toBuffer(proxyData);
+ if (encodedProxyMetadata.byteLength < 1) {
+ throw new Error(
+ `Could not decode Proxy Data. Expected length of encoded data to be at least 1. Got ${
+ encodedProxyMetadata.byteLength
+ }`,
+ );
+ }
+ const encodedAssetProxyId = encodedProxyMetadata.slice(0, 1);
+ const assetProxyId = assetProxyUtils.decodeAssetProxyId(encodedAssetProxyId);
+ return assetProxyId;
+ },
+ decodeProxyData(proxyData: string): ProxyData {
+ const assetProxyId = assetProxyUtils.decodeProxyDataId(proxyData);
+ switch (assetProxyId) {
+ case AssetProxyId.ERC20:
+ const erc20ProxyData = assetProxyUtils.decodeERC20ProxyData(proxyData);
+ const generalizedERC20ProxyData = {
+ assetProxyId,
+ tokenAddress: erc20ProxyData.tokenAddress,
+ };
+ return generalizedERC20ProxyData;
+ case AssetProxyId.ERC721:
+ const erc721ProxyData = assetProxyUtils.decodeERC721ProxyData(proxyData);
+ const generaliedERC721ProxyData = {
+ assetProxyId,
+ tokenAddress: erc721ProxyData.tokenAddress,
+ data: erc721ProxyData.tokenId,
+ };
+ return generaliedERC721ProxyData;
+ default:
+ throw new Error(`Unrecognized asset proxy id: ${assetProxyId}`);
+ }
+ },
};
diff --git a/packages/contracts/src/utils/constants.ts b/packages/contracts/src/utils/constants.ts
index 3fd2a0a3b..483340186 100644
--- a/packages/contracts/src/utils/constants.ts
+++ b/packages/contracts/src/utils/constants.ts
@@ -1,5 +1,5 @@
-import { ZeroEx } from '0x.js';
import { BigNumber } from '@0xproject/utils';
+import { Web3Wrapper } from '@0xproject/web3-wrapper';
import * as ethUtil from 'ethereumjs-util';
import * as _ from 'lodash';
@@ -20,6 +20,7 @@ export const constants = {
INVALID_OPCODE: 'invalid opcode',
REVERT: 'revert',
TESTRPC_NETWORK_ID: 50,
+ AWAIT_TRANSACTION_MINED_MS: 100,
MAX_ETHERTOKEN_WITHDRAW_GAS: 43000,
MAX_TOKEN_TRANSFERFROM_GAS: 80000,
MAX_TOKEN_APPROVE_GAS: 60000,
@@ -30,13 +31,14 @@ export const constants = {
NUM_DUMMY_ERC20_TO_DEPLOY: 3,
NUM_DUMMY_ERC721_TO_DEPLOY: 1,
NUM_ERC721_TOKENS_TO_MINT: 2,
+ NULL_ADDRESS: '0x0000000000000000000000000000000000000000',
TESTRPC_PRIVATE_KEYS: _.map(TESTRPC_PRIVATE_KEYS_STRINGS, privateKeyString => ethUtil.toBuffer(privateKeyString)),
- INITIAL_ERC20_BALANCE: ZeroEx.toBaseUnitAmount(new BigNumber(10000), 18),
- INITIAL_ERC20_ALLOWANCE: ZeroEx.toBaseUnitAmount(new BigNumber(10000), 18),
+ INITIAL_ERC20_BALANCE: Web3Wrapper.toBaseUnitAmount(new BigNumber(10000), 18),
+ INITIAL_ERC20_ALLOWANCE: Web3Wrapper.toBaseUnitAmount(new BigNumber(10000), 18),
STATIC_ORDER_PARAMS: {
- makerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(100), 18),
- takerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(200), 18),
- makerFee: ZeroEx.toBaseUnitAmount(new BigNumber(1), 18),
- takerFee: ZeroEx.toBaseUnitAmount(new BigNumber(1), 18),
+ makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18),
+ takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(200), 18),
+ makerFee: Web3Wrapper.toBaseUnitAmount(new BigNumber(1), 18),
+ takerFee: Web3Wrapper.toBaseUnitAmount(new BigNumber(1), 18),
},
};
diff --git a/packages/contracts/src/utils/coverage.ts b/packages/contracts/src/utils/coverage.ts
new file mode 100644
index 000000000..a37939464
--- /dev/null
+++ b/packages/contracts/src/utils/coverage.ts
@@ -0,0 +1,21 @@
+import { devConstants } from '@0xproject/dev-utils';
+import { CoverageSubprovider, SolCompilerArtifactAdapter } from '@0xproject/sol-cov';
+import * as fs from 'fs';
+import * as _ from 'lodash';
+
+let coverageSubprovider: CoverageSubprovider;
+
+export const coverage = {
+ getCoverageSubproviderSingleton(): CoverageSubprovider {
+ if (_.isUndefined(coverageSubprovider)) {
+ coverageSubprovider = coverage._getCoverageSubprovider();
+ }
+ return coverageSubprovider;
+ },
+ _getCoverageSubprovider(): CoverageSubprovider {
+ const defaultFromAddress = devConstants.TESTRPC_FIRST_ADDRESS;
+ const solCompilerArtifactAdapter = new SolCompilerArtifactAdapter();
+ const subprovider = new CoverageSubprovider(solCompilerArtifactAdapter, defaultFromAddress);
+ return subprovider;
+ },
+};
diff --git a/packages/contracts/src/utils/erc20_wrapper.ts b/packages/contracts/src/utils/erc20_wrapper.ts
index f12571b48..0303649a5 100644
--- a/packages/contracts/src/utils/erc20_wrapper.ts
+++ b/packages/contracts/src/utils/erc20_wrapper.ts
@@ -23,7 +23,7 @@ export class ERC20Wrapper {
}
public async deployDummyTokensAsync(): Promise<DummyERC20TokenContract[]> {
this._dummyTokenContracts = await Promise.all(
- _.times(constants.NUM_DUMMY_ERC20_TO_DEPLOY, () =>
+ _.times(constants.NUM_DUMMY_ERC20_TO_DEPLOY, async () =>
DummyERC20TokenContract.deployFrom0xArtifactAsync(
artifacts.DummyERC20Token,
this._provider,
@@ -45,7 +45,7 @@ export class ERC20Wrapper {
);
return this._proxyContract;
}
- public async setBalancesAndAllowancesAsync() {
+ public async setBalancesAndAllowancesAsync(): Promise<void> {
this._validateDummyTokenContractsExistOrThrow();
this._validateProxyContractExistsOrThrow();
const setBalancePromises: Array<Promise<string>> = [];
@@ -103,12 +103,12 @@ export class ERC20Wrapper {
const tokenAddresses = _.map(this._dummyTokenContracts, dummyTokenContract => dummyTokenContract.address);
return tokenAddresses;
}
- private _validateDummyTokenContractsExistOrThrow() {
+ private _validateDummyTokenContractsExistOrThrow(): void {
if (_.isUndefined(this._dummyTokenContracts)) {
throw new Error('Dummy ERC20 tokens not yet deployed, please call "deployDummyTokensAsync"');
}
}
- private _validateProxyContractExistsOrThrow() {
+ private _validateProxyContractExistsOrThrow(): void {
if (_.isUndefined(this._proxyContract)) {
throw new Error('ERC20 proxy contract not yet deployed, please call "deployProxyAsync"');
}
diff --git a/packages/contracts/src/utils/erc721_wrapper.ts b/packages/contracts/src/utils/erc721_wrapper.ts
index 5b5de262f..aee796e4b 100644
--- a/packages/contracts/src/utils/erc721_wrapper.ts
+++ b/packages/contracts/src/utils/erc721_wrapper.ts
@@ -1,4 +1,4 @@
-import { ZeroEx } from '0x.js';
+import { generatePseudoRandomSalt } from '@0xproject/order-utils';
import { Provider } from '@0xproject/types';
import { BigNumber } from '@0xproject/utils';
import * as _ from 'lodash';
@@ -25,7 +25,7 @@ export class ERC721Wrapper {
}
public async deployDummyTokensAsync(): Promise<DummyERC721TokenContract[]> {
this._dummyTokenContracts = await Promise.all(
- _.times(constants.NUM_DUMMY_ERC721_TO_DEPLOY, () =>
+ _.times(constants.NUM_DUMMY_ERC721_TO_DEPLOY, async () =>
DummyERC721TokenContract.deployFrom0xArtifactAsync(
artifacts.DummyERC721Token,
this._provider,
@@ -45,7 +45,7 @@ export class ERC721Wrapper {
);
return this._proxyContract;
}
- public async setBalancesAndAllowancesAsync() {
+ public async setBalancesAndAllowancesAsync(): Promise<void> {
this._validateDummyTokenContractsExistOrThrow();
this._validateProxyContractExistsOrThrow();
const setBalancePromises: Array<Promise<string>> = [];
@@ -54,7 +54,7 @@ export class ERC721Wrapper {
_.forEach(this._dummyTokenContracts, dummyTokenContract => {
_.forEach(this._tokenOwnerAddresses, tokenOwnerAddress => {
_.forEach(_.range(constants.NUM_ERC721_TOKENS_TO_MINT), () => {
- const tokenId = ZeroEx.generatePseudoRandomSalt();
+ const tokenId = generatePseudoRandomSalt();
setBalancePromises.push(
dummyTokenContract.mint.sendTransactionAsync(tokenOwnerAddress, tokenId, {
from: this._contractOwnerAddress,
@@ -125,17 +125,17 @@ export class ERC721Wrapper {
const tokenAddresses = _.map(this._dummyTokenContracts, dummyTokenContract => dummyTokenContract.address);
return tokenAddresses;
}
- private _validateDummyTokenContractsExistOrThrow() {
+ private _validateDummyTokenContractsExistOrThrow(): void {
if (_.isUndefined(this._dummyTokenContracts)) {
throw new Error('Dummy ERC721 tokens not yet deployed, please call "deployDummyTokensAsync"');
}
}
- private _validateProxyContractExistsOrThrow() {
+ private _validateProxyContractExistsOrThrow(): void {
if (_.isUndefined(this._proxyContract)) {
throw new Error('ERC721 proxy contract not yet deployed, please call "deployProxyAsync"');
}
}
- private _validateBalancesAndAllowancesSetOrThrow() {
+ 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/packages/contracts/src/utils/exchange_wrapper.ts b/packages/contracts/src/utils/exchange_wrapper.ts
index 27fdd698f..c353442f3 100644
--- a/packages/contracts/src/utils/exchange_wrapper.ts
+++ b/packages/contracts/src/utils/exchange_wrapper.ts
@@ -1,5 +1,6 @@
-import { TransactionReceiptWithDecodedLogs, ZeroEx } from '0x.js';
+import { Provider, TransactionReceiptWithDecodedLogs } from '@0xproject/types';
import { BigNumber } from '@0xproject/utils';
+import { Web3Wrapper } from '@0xproject/web3-wrapper';
import * as _ from 'lodash';
import * as Web3 from 'web3';
@@ -9,15 +10,15 @@ import { constants } from './constants';
import { formatters } from './formatters';
import { LogDecoder } from './log_decoder';
import { orderUtils } from './order_utils';
-import { AssetProxyId, SignedOrder, SignedTransaction } from './types';
+import { AssetProxyId, OrderInfo, SignedOrder, SignedTransaction } from './types';
export class ExchangeWrapper {
private _exchange: ExchangeContract;
+ private _web3Wrapper: Web3Wrapper;
private _logDecoder: LogDecoder = new LogDecoder(constants.TESTRPC_NETWORK_ID);
- private _zeroEx: ZeroEx;
- constructor(exchangeContract: ExchangeContract, zeroEx: ZeroEx) {
+ constructor(exchangeContract: ExchangeContract, provider: Provider) {
this._exchange = exchangeContract;
- this._zeroEx = zeroEx;
+ this._web3Wrapper = new Web3Wrapper(provider);
}
public async fillOrderAsync(
signedOrder: SignedOrder,
@@ -196,7 +197,7 @@ export class ExchangeWrapper {
opts: { oldAssetProxyAddressIfExists?: string } = {},
): Promise<TransactionReceiptWithDecodedLogs> {
const oldAssetProxyAddress = _.isUndefined(opts.oldAssetProxyAddressIfExists)
- ? ZeroEx.NULL_ADDRESS
+ ? constants.NULL_ADDRESS
: opts.oldAssetProxyAddressIfExists;
const txHash = await this._exchange.registerAssetProxy.sendTransactionAsync(
assetProxyId,
@@ -225,8 +226,28 @@ export class ExchangeWrapper {
const filledAmount = new BigNumber(await this._exchange.filled.callAsync(orderHashHex));
return filledAmount;
}
- private async _getTxWithDecodedExchangeLogsAsync(txHash: string) {
- const tx = await this._zeroEx.awaitTransactionMinedAsync(txHash);
+ public async getOrderInfoAsync(signedOrder: SignedOrder): Promise<OrderInfo> {
+ const orderInfo = (await this._exchange.getOrderInfo.callAsync(signedOrder)) as OrderInfo;
+ return orderInfo;
+ }
+ 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._getTxWithDecodedExchangeLogsAsync(txHash);
+ return tx;
+ }
+ private async _getTxWithDecodedExchangeLogsAsync(txHash: string): Promise<TransactionReceiptWithDecodedLogs> {
+ const tx = await this._web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
tx.logs = _.filter(tx.logs, log => log.address === this._exchange.address);
tx.logs = _.map(tx.logs, log => this._logDecoder.decodeLogOrThrow(log));
return tx;
diff --git a/packages/contracts/src/utils/formatters.ts b/packages/contracts/src/utils/formatters.ts
index e706c15b5..bfa48d6f1 100644
--- a/packages/contracts/src/utils/formatters.ts
+++ b/packages/contracts/src/utils/formatters.ts
@@ -5,7 +5,7 @@ import { orderUtils } from './order_utils';
import { BatchCancelOrders, BatchFillOrders, MarketBuyOrders, MarketSellOrders, SignedOrder } from './types';
export const formatters = {
- createBatchFill(signedOrders: SignedOrder[], takerAssetFillAmounts: BigNumber[] = []) {
+ createBatchFill(signedOrders: SignedOrder[], takerAssetFillAmounts: BigNumber[] = []): BatchFillOrders {
const batchFill: BatchFillOrders = {
orders: [],
signatures: [],
@@ -21,7 +21,7 @@ export const formatters = {
});
return batchFill;
},
- createMarketSellOrders(signedOrders: SignedOrder[], takerAssetFillAmount: BigNumber) {
+ createMarketSellOrders(signedOrders: SignedOrder[], takerAssetFillAmount: BigNumber): MarketSellOrders {
const marketSellOrders: MarketSellOrders = {
orders: [],
signatures: [],
@@ -34,7 +34,7 @@ export const formatters = {
});
return marketSellOrders;
},
- createMarketBuyOrders(signedOrders: SignedOrder[], makerAssetFillAmount: BigNumber) {
+ createMarketBuyOrders(signedOrders: SignedOrder[], makerAssetFillAmount: BigNumber): MarketBuyOrders {
const marketBuyOrders: MarketBuyOrders = {
orders: [],
signatures: [],
@@ -47,7 +47,7 @@ export const formatters = {
});
return marketBuyOrders;
},
- createBatchCancel(signedOrders: SignedOrder[]) {
+ createBatchCancel(signedOrders: SignedOrder[]): BatchCancelOrders {
const batchCancel: BatchCancelOrders = {
orders: [],
};
diff --git a/packages/contracts/src/utils/match_order_tester.ts b/packages/contracts/src/utils/match_order_tester.ts
new file mode 100644
index 000000000..30039937f
--- /dev/null
+++ b/packages/contracts/src/utils/match_order_tester.ts
@@ -0,0 +1,356 @@
+import { BlockchainLifecycle } from '@0xproject/dev-utils';
+import { LogWithDecodedArgs } from '@0xproject/types';
+import { BigNumber } from '@0xproject/utils';
+import * as chai from 'chai';
+import ethUtil = require('ethereumjs-util');
+import * as _ from 'lodash';
+
+import { DummyERC20TokenContract } from '../contract_wrappers/generated/dummy_e_r_c20_token';
+import { DummyERC721TokenContract } from '../contract_wrappers/generated/dummy_e_r_c721_token';
+import { ERC20ProxyContract } from '../contract_wrappers/generated/e_r_c20_proxy';
+import { ERC721ProxyContract } from '../contract_wrappers/generated/e_r_c721_proxy';
+import {
+ CancelContractEventArgs,
+ ExchangeContract,
+ FillContractEventArgs,
+} from '../contract_wrappers/generated/exchange';
+import { assetProxyUtils } from '../utils/asset_proxy_utils';
+import { chaiSetup } from '../utils/chai_setup';
+import { constants } from '../utils/constants';
+import { crypto } from '../utils/crypto';
+import { ERC20Wrapper } from '../utils/erc20_wrapper';
+import { ERC721Wrapper } from '../utils/erc721_wrapper';
+import { ExchangeWrapper } from '../utils/exchange_wrapper';
+import { OrderFactory } from '../utils/order_factory';
+import { orderUtils } from '../utils/order_utils';
+import {
+ AssetProxyId,
+ ContractName,
+ ERC20BalancesByOwner,
+ ERC721TokenIdsByOwner,
+ ExchangeStatus,
+ SignedOrder,
+ TransferAmountsByMatchOrders as TransferAmounts,
+} from '../utils/types';
+import { provider, web3Wrapper } from '../utils/web3_wrapper';
+
+chaiSetup.configure();
+const expect = chai.expect;
+const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
+
+export class MatchOrderTester {
+ private _exchangeWrapper: ExchangeWrapper;
+ private _erc20Wrapper: ERC20Wrapper;
+ private _erc721Wrapper: ERC721Wrapper;
+ private _feeTokenAddress: string;
+
+ /// @dev Compares a pair of ERC20 balances and a pair of ERC721 token owners.
+ /// @param expectedNewERC20BalancesByOwner Expected ERC20 balances.
+ /// @param realERC20BalancesByOwner Actual ERC20 balances.
+ /// @param expectedNewERC721TokenIdsByOwner Expected ERC721 token owners.
+ /// @param realERC721TokenIdsByOwner Actual ERC20 token owners.
+ /// @return True only if ERC20 balances match and ERC721 token owners match.
+ private static _compareExpectedAndRealBalances(
+ expectedNewERC20BalancesByOwner: ERC20BalancesByOwner,
+ realERC20BalancesByOwner: ERC20BalancesByOwner,
+ expectedNewERC721TokenIdsByOwner: ERC721TokenIdsByOwner,
+ realERC721TokenIdsByOwner: ERC721TokenIdsByOwner,
+ ): boolean {
+ // ERC20 Balances
+ const doesErc20BalancesMatch = _.isEqual(expectedNewERC20BalancesByOwner, realERC20BalancesByOwner);
+ if (!doesErc20BalancesMatch) {
+ return false;
+ }
+ // ERC721 Token Ids
+ const sortedExpectedNewERC721TokenIdsByOwner = _.mapValues(
+ expectedNewERC721TokenIdsByOwner,
+ tokenIdsByOwner => {
+ _.mapValues(tokenIdsByOwner, tokenIds => {
+ _.sortBy(tokenIds);
+ });
+ },
+ );
+ const sortedNewERC721TokenIdsByOwner = _.mapValues(realERC721TokenIdsByOwner, tokenIdsByOwner => {
+ _.mapValues(tokenIdsByOwner, tokenIds => {
+ _.sortBy(tokenIds);
+ });
+ });
+ const doesErc721TokenIdsMatch = _.isEqual(
+ sortedExpectedNewERC721TokenIdsByOwner,
+ sortedNewERC721TokenIdsByOwner,
+ );
+ return doesErc721TokenIdsMatch;
+ }
+ /// @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 validates results.
+ /// Validation either succeeds or throws.
+ /// @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 initialTakerAssetFilledAmountLeft Current amount the left order has been filled.
+ /// @param initialTakerAssetFilledAmountRight Current amount the right order has been filled.
+ /// @return New ERC20 balances & ERC721 token owners.
+ public async matchOrdersAndVerifyBalancesAsync(
+ signedOrderLeft: SignedOrder,
+ signedOrderRight: SignedOrder,
+ takerAddress: string,
+ erc20BalancesByOwner: ERC20BalancesByOwner,
+ erc721TokenIdsByOwner: ERC721TokenIdsByOwner,
+ initialTakerAssetFilledAmountLeft?: BigNumber,
+ initialTakerAssetFilledAmountRight?: BigNumber,
+ ): Promise<[ERC20BalancesByOwner, ERC721TokenIdsByOwner]> {
+ // Test setup & verify preconditions
+ const makerAddressLeft = signedOrderLeft.makerAddress;
+ const makerAddressRight = signedOrderRight.makerAddress;
+ const feeRecipientAddressLeft = signedOrderLeft.feeRecipientAddress;
+ const feeRecipientAddressRight = signedOrderRight.feeRecipientAddress;
+ // Verify Left order preconditions
+ const orderTakerAssetFilledAmountLeft = await this._exchangeWrapper.getTakerAssetFilledAmountAsync(
+ orderUtils.getOrderHashHex(signedOrderLeft),
+ );
+ const expectedOrderFilledAmountLeft = initialTakerAssetFilledAmountLeft
+ ? initialTakerAssetFilledAmountLeft
+ : new BigNumber(0);
+ expect(expectedOrderFilledAmountLeft).to.be.bignumber.equal(orderTakerAssetFilledAmountLeft);
+ // Verify Right order preconditions
+ const orderTakerAssetFilledAmountRight = await this._exchangeWrapper.getTakerAssetFilledAmountAsync(
+ orderUtils.getOrderHashHex(signedOrderRight),
+ );
+ const expectedOrderFilledAmountRight = initialTakerAssetFilledAmountRight
+ ? initialTakerAssetFilledAmountRight
+ : new BigNumber(0);
+ expect(expectedOrderFilledAmountRight).to.be.bignumber.equal(orderTakerAssetFilledAmountRight);
+ // Match left & right orders
+ await this._exchangeWrapper.matchOrdersAsync(signedOrderLeft, signedOrderRight, takerAddress);
+ const newERC20BalancesByOwner = await this._erc20Wrapper.getBalancesAsync();
+ const newERC721TokenIdsByOwner = await this._erc721Wrapper.getBalancesAsync();
+ // Calculate expected balance changes
+ const expectedTransferAmounts = await this._calculateExpectedTransferAmountsAsync(
+ signedOrderLeft,
+ signedOrderRight,
+ orderTakerAssetFilledAmountLeft,
+ orderTakerAssetFilledAmountRight,
+ );
+ let expectedERC20BalancesByOwner: ERC20BalancesByOwner;
+ let expectedERC721TokenIdsByOwner: ERC721TokenIdsByOwner;
+ [expectedERC20BalancesByOwner, expectedERC721TokenIdsByOwner] = this._calculateExpectedBalances(
+ signedOrderLeft,
+ signedOrderRight,
+ takerAddress,
+ erc20BalancesByOwner,
+ erc721TokenIdsByOwner,
+ expectedTransferAmounts,
+ );
+ // Assert our expected balances are equal to the actual balances
+ const didExpectedBalancesMatchRealBalances = MatchOrderTester._compareExpectedAndRealBalances(
+ expectedERC20BalancesByOwner,
+ newERC20BalancesByOwner,
+ expectedERC721TokenIdsByOwner,
+ newERC721TokenIdsByOwner,
+ );
+ expect(didExpectedBalancesMatchRealBalances).to.be.true();
+ return [newERC20BalancesByOwner, newERC721TokenIdsByOwner];
+ }
+ /// @dev Calculates expected transfer amounts between order makers, fee recipients, and
+ /// the taker when two orders are matched.
+ /// @param signedOrderLeft First matched order.
+ /// @param signedOrderRight Second matched order.
+ /// @param orderTakerAssetFilledAmountLeft How much left order has been filled, prior to matching orders.
+ /// @param orderTakerAssetFilledAmountRight How much the right order has been filled, prior to matching orders.
+ /// @return TransferAmounts A struct containing the expected transfer amounts.
+ private async _calculateExpectedTransferAmountsAsync(
+ signedOrderLeft: SignedOrder,
+ signedOrderRight: SignedOrder,
+ orderTakerAssetFilledAmountLeft: BigNumber,
+ orderTakerAssetFilledAmountRight: BigNumber,
+ ): Promise<TransferAmounts> {
+ let amountBoughtByLeftMaker = await this._exchangeWrapper.getTakerAssetFilledAmountAsync(
+ orderUtils.getOrderHashHex(signedOrderLeft),
+ );
+ amountBoughtByLeftMaker = amountBoughtByLeftMaker.minus(orderTakerAssetFilledAmountLeft);
+ const amountSoldByLeftMaker = amountBoughtByLeftMaker
+ .times(signedOrderLeft.makerAssetAmount)
+ .dividedToIntegerBy(signedOrderLeft.takerAssetAmount);
+ const amountReceivedByRightMaker = amountBoughtByLeftMaker
+ .times(signedOrderRight.takerAssetAmount)
+ .dividedToIntegerBy(signedOrderRight.makerAssetAmount);
+ const amountReceivedByTaker = amountSoldByLeftMaker.minus(amountReceivedByRightMaker);
+ let amountBoughtByRightMaker = await this._exchangeWrapper.getTakerAssetFilledAmountAsync(
+ orderUtils.getOrderHashHex(signedOrderRight),
+ );
+ amountBoughtByRightMaker = amountBoughtByRightMaker.minus(orderTakerAssetFilledAmountRight);
+ const amountSoldByRightMaker = amountBoughtByRightMaker
+ .times(signedOrderRight.makerAssetAmount)
+ .dividedToIntegerBy(signedOrderRight.takerAssetAmount);
+ const amountReceivedByLeftMaker = amountSoldByRightMaker;
+ const feePaidByLeftMaker = signedOrderLeft.makerFee
+ .times(amountSoldByLeftMaker)
+ .dividedToIntegerBy(signedOrderLeft.makerAssetAmount);
+ const feePaidByRightMaker = signedOrderRight.makerFee
+ .times(amountSoldByRightMaker)
+ .dividedToIntegerBy(signedOrderRight.makerAssetAmount);
+ const feePaidByTakerLeft = signedOrderLeft.takerFee
+ .times(amountSoldByLeftMaker)
+ .dividedToIntegerBy(signedOrderLeft.makerAssetAmount);
+ const feePaidByTakerRight = signedOrderRight.takerFee
+ .times(amountSoldByRightMaker)
+ .dividedToIntegerBy(signedOrderRight.makerAssetAmount);
+ const totalFeePaidByTaker = feePaidByTakerLeft.add(feePaidByTakerRight);
+ const feeReceivedLeft = feePaidByLeftMaker.add(feePaidByTakerLeft);
+ const feeReceivedRight = feePaidByRightMaker.add(feePaidByTakerRight);
+ // Return values
+ const expectedTransferAmounts = {
+ // Left Maker
+ amountBoughtByLeftMaker,
+ amountSoldByLeftMaker,
+ amountReceivedByLeftMaker,
+ feePaidByLeftMaker,
+ // Right Maker
+ amountBoughtByRightMaker,
+ amountSoldByRightMaker,
+ amountReceivedByRightMaker,
+ feePaidByRightMaker,
+ // Taker
+ amountReceivedByTaker,
+ feePaidByTakerLeft,
+ feePaidByTakerRight,
+ totalFeePaidByTaker,
+ // Fee Recipients
+ feeReceivedLeft,
+ feeReceivedRight,
+ };
+ return expectedTransferAmounts;
+ }
+ /// @dev Calculates the expected balances of order makers, fee recipients, and the taker,
+ /// as a result of matching two orders.
+ /// @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 A struct containing the expected transfer amounts.
+ /// @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 = assetProxyUtils.decodeProxyDataId(signedOrderLeft.makerAssetData);
+ if (makerAssetProxyIdLeft === AssetProxyId.ERC20) {
+ // Decode asset data
+ const erc20ProxyData = assetProxyUtils.decodeERC20ProxyData(signedOrderLeft.makerAssetData);
+ const makerAssetAddressLeft = erc20ProxyData.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.amountReceivedByRightMaker,
+ );
+ // Taker
+ expectedNewERC20BalancesByOwner[takerAddress][makerAssetAddressLeft] = expectedNewERC20BalancesByOwner[
+ takerAddress
+ ][makerAssetAddressLeft].add(expectedTransferAmounts.amountReceivedByTaker);
+ } else if (makerAssetProxyIdLeft === AssetProxyId.ERC721) {
+ // Decode asset data
+ const erc721ProxyData = assetProxyUtils.decodeERC721ProxyData(signedOrderLeft.makerAssetData);
+ const makerAssetAddressLeft = erc721ProxyData.tokenAddress;
+ const makerAssetIdLeft = erc721ProxyData.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 = assetProxyUtils.decodeProxyDataId(signedOrderLeft.takerAssetData);
+ if (takerAssetProxyIdLeft === AssetProxyId.ERC20) {
+ // Decode asset data
+ const erc20ProxyData = assetProxyUtils.decodeERC20ProxyData(signedOrderLeft.takerAssetData);
+ const takerAssetAddressLeft = erc20ProxyData.tokenAddress;
+ const makerAssetAddressRight = takerAssetAddressLeft;
+ // Left Maker
+ expectedNewERC20BalancesByOwner[makerAddressLeft][takerAssetAddressLeft] = expectedNewERC20BalancesByOwner[
+ makerAddressLeft
+ ][takerAssetAddressLeft].add(expectedTransferAmounts.amountReceivedByLeftMaker);
+ // Right Maker
+ expectedNewERC20BalancesByOwner[makerAddressRight][
+ makerAssetAddressRight
+ ] = expectedNewERC20BalancesByOwner[makerAddressRight][makerAssetAddressRight].minus(
+ expectedTransferAmounts.amountSoldByRightMaker,
+ );
+ } else if (takerAssetProxyIdLeft === AssetProxyId.ERC721) {
+ // Decode asset data
+ const erc721ProxyData = assetProxyUtils.decodeERC721ProxyData(signedOrderRight.makerAssetData);
+ const makerAssetAddressRight = erc721ProxyData.tokenAddress;
+ const makerAssetIdRight = erc721ProxyData.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.totalFeePaidByTaker);
+ // Left Fee Recipient Fees
+ expectedNewERC20BalancesByOwner[feeRecipientAddressLeft][
+ this._feeTokenAddress
+ ] = expectedNewERC20BalancesByOwner[feeRecipientAddressLeft][this._feeTokenAddress].add(
+ expectedTransferAmounts.feeReceivedLeft,
+ );
+ // Right Fee Recipient Fees
+ expectedNewERC20BalancesByOwner[feeRecipientAddressRight][
+ this._feeTokenAddress
+ ] = expectedNewERC20BalancesByOwner[feeRecipientAddressRight][this._feeTokenAddress].add(
+ expectedTransferAmounts.feeReceivedRight,
+ );
+
+ return [expectedNewERC20BalancesByOwner, expectedNewERC721TokenIdsByOwner];
+ }
+}
diff --git a/packages/contracts/src/utils/order_factory.ts b/packages/contracts/src/utils/order_factory.ts
index 044e9b865..86b3d5ac7 100644
--- a/packages/contracts/src/utils/order_factory.ts
+++ b/packages/contracts/src/utils/order_factory.ts
@@ -1,7 +1,8 @@
-import { ZeroEx } from '0x.js';
+import { generatePseudoRandomSalt } from '@0xproject/order-utils';
import { BigNumber } from '@0xproject/utils';
import * as _ from 'lodash';
+import { constants } from './constants';
import { orderUtils } from './order_utils';
import { signingUtils } from './signing_utils';
import { SignatureType, SignedOrder, UnsignedOrder } from './types';
@@ -19,10 +20,10 @@ export class OrderFactory {
): SignedOrder {
const randomExpiration = new BigNumber(Math.floor((Date.now() + Math.random() * 100000000000) / 1000));
const order = ({
- senderAddress: ZeroEx.NULL_ADDRESS,
+ senderAddress: constants.NULL_ADDRESS,
expirationTimeSeconds: randomExpiration,
- salt: ZeroEx.generatePseudoRandomSalt(),
- takerAddress: ZeroEx.NULL_ADDRESS,
+ salt: generatePseudoRandomSalt(),
+ takerAddress: constants.NULL_ADDRESS,
...this._defaultOrderParams,
...customOrderParams,
} as any) as UnsignedOrder;
diff --git a/packages/contracts/src/utils/order_utils.ts b/packages/contracts/src/utils/order_utils.ts
index 10bbf4f7c..8adc6b735 100644
--- a/packages/contracts/src/utils/order_utils.ts
+++ b/packages/contracts/src/utils/order_utils.ts
@@ -4,7 +4,7 @@ import ethUtil = require('ethereumjs-util');
import * as _ from 'lodash';
import { crypto } from './crypto';
-import { OrderStruct, SignatureType, SignedOrder, UnsignedOrder } from './types';
+import { CancelOrder, MatchOrder, OrderStruct, SignatureType, SignedOrder, UnsignedOrder } from './types';
export const orderUtils = {
createFill: (signedOrder: SignedOrder, takerAssetFillAmount?: BigNumber) => {
@@ -15,7 +15,7 @@ export const orderUtils = {
};
return fill;
},
- createCancel(signedOrder: SignedOrder, takerAssetCancelAmount?: BigNumber) {
+ createCancel(signedOrder: SignedOrder, takerAssetCancelAmount?: BigNumber): CancelOrder {
const cancel = {
order: orderUtils.getOrderStruct(signedOrder),
takerAssetCancelAmount: takerAssetCancelAmount || signedOrder.takerAssetAmount,
@@ -80,4 +80,13 @@ export const orderUtils = {
const orderHashHex = `0x${orderHashBuff.toString('hex')}`;
return orderHashHex;
},
+ createMatchOrders(signedOrderLeft: SignedOrder, signedOrderRight: SignedOrder): MatchOrder {
+ const fill = {
+ left: orderUtils.getOrderStruct(signedOrderLeft),
+ right: orderUtils.getOrderStruct(signedOrderRight),
+ leftSignature: signedOrderLeft.signature,
+ rightSignature: signedOrderRight.signature,
+ };
+ return fill;
+ },
};
diff --git a/packages/contracts/src/utils/transaction_factory.ts b/packages/contracts/src/utils/transaction_factory.ts
index 3a4f48330..941bff96d 100644
--- a/packages/contracts/src/utils/transaction_factory.ts
+++ b/packages/contracts/src/utils/transaction_factory.ts
@@ -1,4 +1,4 @@
-import { ZeroEx } from '0x.js';
+import { generatePseudoRandomSalt } from '@0xproject/order-utils';
import { BigNumber } from '@0xproject/utils';
import * as ethUtil from 'ethereumjs-util';
@@ -20,7 +20,7 @@ export class TransactionFactory {
data: string,
signatureType: SignatureType = SignatureType.Ecrecover,
): SignedTransaction {
- const salt = ZeroEx.generatePseudoRandomSalt();
+ const salt = generatePseudoRandomSalt();
const txHash = crypto.solSHA3([this._exchangeAddress, salt, ethUtil.toBuffer(data)]);
const signature = signingUtils.signMessage(txHash, this._privateKey, signatureType);
const signedTx = {
diff --git a/packages/contracts/src/utils/types.ts b/packages/contracts/src/utils/types.ts
index 234b14ef9..ef86b4f38 100644
--- a/packages/contracts/src/utils/types.ts
+++ b/packages/contracts/src/utils/types.ts
@@ -74,12 +74,22 @@ export interface Token {
swarmHash: string;
}
-export enum ExchangeContractErrs {
- ERROR_ORDER_EXPIRED,
- ERROR_ORDER_FULLY_FILLED,
- ERROR_ORDER_CANCELLED,
- ERROR_ROUNDING_ERROR_TOO_LARGE,
- ERROR_INSUFFICIENT_BALANCE_OR_ALLOWANCE,
+export enum ExchangeStatus {
+ INVALID,
+ SUCCESS,
+ ROUNDING_ERROR_TOO_LARGE,
+ INSUFFICIENT_BALANCE_OR_ALLOWANCE,
+ TAKER_ASSET_FILL_AMOUNT_TOO_LOW,
+ INVALID_SIGNATURE,
+ INVALID_SENDER,
+ INVALID_TAKER,
+ INVALID_MAKER,
+ ORDER_INVALID_MAKER_ASSET_AMOUNT,
+ ORDER_INVALID_TAKER_ASSET_AMOUNT,
+ ORDER_FILLABLE,
+ ORDER_EXPIRED,
+ ORDER_FULLY_FILLED,
+ ORDER_CANCELLED,
}
export enum ContractName {
@@ -143,3 +153,59 @@ export interface SignedTransaction {
data: string;
signature: string;
}
+
+export interface TransferAmountsByMatchOrders {
+ // Left Maker
+ amountBoughtByLeftMaker: BigNumber;
+ amountSoldByLeftMaker: BigNumber;
+ amountReceivedByLeftMaker: BigNumber;
+ feePaidByLeftMaker: BigNumber;
+ // Right Maker
+ amountBoughtByRightMaker: BigNumber;
+ amountSoldByRightMaker: BigNumber;
+ amountReceivedByRightMaker: BigNumber;
+ feePaidByRightMaker: BigNumber;
+ // Taker
+ amountReceivedByTaker: BigNumber;
+ feePaidByTakerLeft: BigNumber;
+ feePaidByTakerRight: BigNumber;
+ totalFeePaidByTaker: BigNumber;
+ // Fee Recipients
+ feeReceivedLeft: BigNumber;
+ feeReceivedRight: BigNumber;
+}
+
+export interface OrderInfo {
+ orderStatus: number;
+ orderHash: string;
+ orderTakerAssetFilledAmount: BigNumber;
+}
+
+export interface ERC20ProxyData {
+ assetProxyId: AssetProxyId;
+ tokenAddress: string;
+}
+
+export interface ERC721ProxyData {
+ assetProxyId: AssetProxyId;
+ tokenAddress: string;
+ tokenId: BigNumber;
+}
+
+export interface ProxyData {
+ assetProxyId: AssetProxyId;
+ tokenAddress?: string;
+ data?: any;
+}
+
+export interface CancelOrder {
+ order: OrderStruct;
+ takerAssetCancelAmount: BigNumber;
+}
+
+export interface MatchOrder {
+ left: OrderStruct;
+ right: OrderStruct;
+ leftSignature: string;
+ rightSignature: string;
+}
diff --git a/packages/contracts/src/utils/web3_wrapper.ts b/packages/contracts/src/utils/web3_wrapper.ts
index ed1c488a2..02595506b 100644
--- a/packages/contracts/src/utils/web3_wrapper.ts
+++ b/packages/contracts/src/utils/web3_wrapper.ts
@@ -1,12 +1,19 @@
-import { devConstants, web3Factory } from '@0xproject/dev-utils';
+import { devConstants, env, EnvVars, web3Factory } from '@0xproject/dev-utils';
+import { prependSubprovider } from '@0xproject/subproviders';
import { Provider } from '@0xproject/types';
import { Web3Wrapper } from '@0xproject/web3-wrapper';
+import { coverage } from './coverage';
+
export const txDefaults = {
from: devConstants.TESTRPC_FIRST_ADDRESS,
gas: devConstants.GAS_ESTIMATE,
};
const providerConfigs = { shouldUseInProcessGanache: true };
-export const web3 = web3Factory.create(providerConfigs);
-export const provider = web3.currentProvider;
+export const provider = web3Factory.getRpcProvider(providerConfigs);
+const isCoverageEnabled = env.parseBoolean(EnvVars.SolidityCoverage);
+if (isCoverageEnabled) {
+ const coverageSubprovider = coverage.getCoverageSubproviderSingleton();
+ prependSubprovider(provider, coverageSubprovider);
+}
export const web3Wrapper = new Web3Wrapper(provider);
diff --git a/packages/contracts/test/asset_proxy/authorizable.ts b/packages/contracts/test/asset_proxy/authorizable.ts
index 39672bea9..87f89c513 100644
--- a/packages/contracts/test/asset_proxy/authorizable.ts
+++ b/packages/contracts/test/asset_proxy/authorizable.ts
@@ -1,6 +1,7 @@
import { BlockchainLifecycle, devConstants, web3Factory } from '@0xproject/dev-utils';
import { Web3Wrapper } from '@0xproject/web3-wrapper';
import * as chai from 'chai';
+import 'make-promises-safe';
import * as Web3 from 'web3';
import { MixinAuthorizableContract } from '../../src/contract_wrappers/generated/mixin_authorizable';
@@ -19,6 +20,12 @@ describe('Authorizable', () => {
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 = accounts[0];
notOwner = accounts[1];
@@ -41,12 +48,18 @@ describe('Authorizable', () => {
).to.be.rejectedWith(constants.REVERT);
});
it('should allow owner to add an authorized address', async () => {
- await authorizable.addAuthorizedAddress.sendTransactionAsync(address, { from: owner });
+ 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 authorizable.addAuthorizedAddress.sendTransactionAsync(address, { from: owner });
+ await web3Wrapper.awaitTransactionSuccessAsync(
+ await authorizable.addAuthorizedAddress.sendTransactionAsync(address, { from: owner }),
+ constants.AWAIT_TRANSACTION_MINED_MS,
+ );
return expect(
authorizable.addAuthorizedAddress.sendTransactionAsync(address, { from: owner }),
).to.be.rejectedWith(constants.REVERT);
@@ -55,7 +68,10 @@ describe('Authorizable', () => {
describe('removeAuthorizedAddress', () => {
it('should throw if not called by owner', async () => {
- await authorizable.addAuthorizedAddress.sendTransactionAsync(address, { from: owner });
+ await web3Wrapper.awaitTransactionSuccessAsync(
+ await authorizable.addAuthorizedAddress.sendTransactionAsync(address, { from: owner }),
+ constants.AWAIT_TRANSACTION_MINED_MS,
+ );
return expect(
authorizable.removeAuthorizedAddress.sendTransactionAsync(address, {
from: notOwner,
@@ -64,10 +80,16 @@ describe('Authorizable', () => {
});
it('should allow owner to remove an authorized address', async () => {
- await authorizable.addAuthorizedAddress.sendTransactionAsync(address, { from: owner });
- await authorizable.removeAuthorizedAddress.sendTransactionAsync(address, {
- from: owner,
- });
+ 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();
});
@@ -85,16 +107,22 @@ describe('Authorizable', () => {
it('should return all authorized addresses', async () => {
const initial = await authorizable.getAuthorizedAddresses.callAsync();
expect(initial).to.have.length(0);
- await authorizable.addAuthorizedAddress.sendTransactionAsync(address, {
- from: owner,
- });
+ 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 authorizable.removeAuthorizedAddress.sendTransactionAsync(address, {
- from: owner,
- });
+ 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/packages/contracts/test/asset_proxy/proxies.ts b/packages/contracts/test/asset_proxy/proxies.ts
index e7ef9a0d3..9bcdfa2b8 100644
--- a/packages/contracts/test/asset_proxy/proxies.ts
+++ b/packages/contracts/test/asset_proxy/proxies.ts
@@ -1,4 +1,3 @@
-import { ZeroEx } from '0x.js';
import { BlockchainLifecycle } from '@0xproject/dev-utils';
import { BigNumber } from '@0xproject/utils';
import * as chai from 'chai';
@@ -37,8 +36,12 @@ describe('Asset Transfer Proxies', () => {
let erc721Wrapper: ERC721Wrapper;
let erc721MakerTokenId: BigNumber;
- let zeroEx: ZeroEx;
-
+ before(async () => {
+ await blockchainLifecycle.startAsync();
+ });
+ after(async () => {
+ await blockchainLifecycle.revertAsync();
+ });
before(async () => {
const accounts = await web3Wrapper.getAvailableAddressesAsync();
const usedAddresses = ([owner, notAuthorized, exchangeAddress, makerAddress, takerAddress] = accounts);
@@ -49,22 +52,24 @@ describe('Asset Transfer Proxies', () => {
[zrxToken] = await erc20Wrapper.deployDummyTokensAsync();
erc20Proxy = await erc20Wrapper.deployProxyAsync();
await erc20Wrapper.setBalancesAndAllowancesAsync();
- await erc20Proxy.addAuthorizedAddress.sendTransactionAsync(exchangeAddress, {
- from: owner,
- });
+ await web3Wrapper.awaitTransactionSuccessAsync(
+ await erc20Proxy.addAuthorizedAddress.sendTransactionAsync(exchangeAddress, {
+ from: owner,
+ }),
+ constants.AWAIT_TRANSACTION_MINED_MS,
+ );
[erc721Token] = await erc721Wrapper.deployDummyTokensAsync();
erc721Proxy = await erc721Wrapper.deployProxyAsync();
await erc721Wrapper.setBalancesAndAllowancesAsync();
const erc721Balances = await erc721Wrapper.getBalancesAsync();
erc721MakerTokenId = erc721Balances[makerAddress][erc721Token.address][0];
- await erc721Proxy.addAuthorizedAddress.sendTransactionAsync(exchangeAddress, {
- from: owner,
- });
-
- zeroEx = new ZeroEx(provider, {
- networkId: constants.TESTRPC_NETWORK_ID,
- });
+ await web3Wrapper.awaitTransactionSuccessAsync(
+ await erc721Proxy.addAuthorizedAddress.sendTransactionAsync(exchangeAddress, {
+ from: owner,
+ }),
+ constants.AWAIT_TRANSACTION_MINED_MS,
+ );
});
beforeEach(async () => {
await blockchainLifecycle.startAsync();
@@ -80,12 +85,15 @@ describe('Asset Transfer Proxies', () => {
// Perform a transfer from makerAddress to takerAddress
const erc20Balances = await erc20Wrapper.getBalancesAsync();
const amount = new BigNumber(10);
- await erc20Proxy.transferFrom.sendTransactionAsync(
- encodedProxyMetadata,
- makerAddress,
- takerAddress,
- amount,
- { from: exchangeAddress },
+ await web3Wrapper.awaitTransactionSuccessAsync(
+ await erc20Proxy.transferFrom.sendTransactionAsync(
+ encodedProxyMetadata,
+ makerAddress,
+ takerAddress,
+ amount,
+ { from: exchangeAddress },
+ ),
+ constants.AWAIT_TRANSACTION_MINED_MS,
);
// Verify transfer was successful
const newBalances = await erc20Wrapper.getBalancesAsync();
@@ -103,12 +111,15 @@ describe('Asset Transfer Proxies', () => {
// Perform a transfer from makerAddress to takerAddress
const erc20Balances = await erc20Wrapper.getBalancesAsync();
const amount = new BigNumber(0);
- await erc20Proxy.transferFrom.sendTransactionAsync(
- encodedProxyMetadata,
- makerAddress,
- takerAddress,
- amount,
- { from: exchangeAddress },
+ await web3Wrapper.awaitTransactionSuccessAsync(
+ await erc20Proxy.transferFrom.sendTransactionAsync(
+ encodedProxyMetadata,
+ makerAddress,
+ takerAddress,
+ amount,
+ { from: exchangeAddress },
+ ),
+ constants.AWAIT_TRANSACTION_MINED_MS,
);
// Verify transfer was successful
const newBalances = await erc20Wrapper.getBalancesAsync();
@@ -126,9 +137,12 @@ describe('Asset Transfer Proxies', () => {
// Create allowance less than transfer amount. Set allowance on proxy.
const allowance = new BigNumber(0);
const transferAmount = new BigNumber(10);
- await zrxToken.approve.sendTransactionAsync(erc20Proxy.address, allowance, {
- from: makerAddress,
- });
+ await web3Wrapper.awaitTransactionSuccessAsync(
+ await zrxToken.approve.sendTransactionAsync(erc20Proxy.address, allowance, {
+ from: makerAddress,
+ }),
+ constants.AWAIT_TRANSACTION_MINED_MS,
+ );
// Perform a transfer; expect this to fail.
return expect(
erc20Proxy.transferFrom.sendTransactionAsync(
@@ -179,7 +193,10 @@ describe('Asset Transfer Proxies', () => {
amounts,
{ from: exchangeAddress },
);
- const res = await zeroEx.awaitTransactionMinedAsync(txHash);
+ const res = await web3Wrapper.awaitTransactionSuccessAsync(
+ txHash,
+ constants.AWAIT_TRANSACTION_MINED_MS,
+ );
const newBalances = await erc20Wrapper.getBalancesAsync();
expect(res.logs.length).to.equal(numTransfers);
@@ -200,7 +217,7 @@ describe('Asset Transfer Proxies', () => {
const toAddresses = _.times(numTransfers, () => takerAddress);
const amounts = _.times(numTransfers, () => amount);
- expect(
+ return expect(
erc20Proxy.batchTransferFrom.sendTransactionAsync(
assetMetadata,
fromAddresses,
@@ -232,12 +249,15 @@ describe('Asset Transfer Proxies', () => {
// Perform a transfer from makerAddress to takerAddress
const erc20Balances = await erc20Wrapper.getBalancesAsync();
const amount = new BigNumber(1);
- await erc721Proxy.transferFrom.sendTransactionAsync(
- encodedProxyMetadata,
- makerAddress,
- takerAddress,
- amount,
- { from: exchangeAddress },
+ await web3Wrapper.awaitTransactionSuccessAsync(
+ await erc721Proxy.transferFrom.sendTransactionAsync(
+ encodedProxyMetadata,
+ makerAddress,
+ takerAddress,
+ amount,
+ { from: exchangeAddress },
+ ),
+ constants.AWAIT_TRANSACTION_MINED_MS,
);
// Verify transfer was successful
const newOwnerMakerAsset = await erc721Token.ownerOf.callAsync(erc721MakerTokenId);
@@ -297,9 +317,12 @@ describe('Asset Transfer Proxies', () => {
erc721MakerTokenId,
);
// Remove transfer approval for makerAddress.
- await erc721Token.setApprovalForAll.sendTransactionAsync(erc721Proxy.address, false, {
- from: makerAddress,
- });
+ await web3Wrapper.awaitTransactionSuccessAsync(
+ await erc721Token.setApprovalForAll.sendTransactionAsync(erc721Proxy.address, false, {
+ from: makerAddress,
+ }),
+ constants.AWAIT_TRANSACTION_MINED_MS,
+ );
// Perform a transfer; expect this to fail.
const amount = new BigNumber(1);
return expect(
@@ -356,7 +379,10 @@ describe('Asset Transfer Proxies', () => {
amounts,
{ from: exchangeAddress },
);
- const res = await zeroEx.awaitTransactionMinedAsync(txHash);
+ const res = await web3Wrapper.awaitTransactionSuccessAsync(
+ txHash,
+ constants.AWAIT_TRANSACTION_MINED_MS,
+ );
expect(res.logs.length).to.equal(numTransfers);
const newOwnerMakerAssetA = await erc721Token.ownerOf.callAsync(makerTokenIdA);
@@ -378,7 +404,7 @@ describe('Asset Transfer Proxies', () => {
const toAddresses = _.times(numTransfers, () => takerAddress);
const amounts = _.times(numTransfers, () => new BigNumber(1));
- expect(
+ return expect(
erc721Proxy.batchTransferFrom.sendTransactionAsync(
assetMetadata,
fromAddresses,
diff --git a/packages/contracts/test/ether_token.ts b/packages/contracts/test/ether_token.ts
index 213c7c8a6..5c8a5e199 100644
--- a/packages/contracts/test/ether_token.ts
+++ b/packages/contracts/test/ether_token.ts
@@ -1,8 +1,9 @@
-import { ContractWrappersError, ZeroEx } from '0x.js';
+import { ContractWrappers, ContractWrappersError } from '@0xproject/contract-wrappers';
import { BlockchainLifecycle, devConstants, web3Factory } from '@0xproject/dev-utils';
import { BigNumber, promisify } from '@0xproject/utils';
import { Web3Wrapper } from '@0xproject/web3-wrapper';
import * as chai from 'chai';
+import 'make-promises-safe';
import { WETH9Contract } from '../src/contract_wrappers/generated/weth9';
import { artifacts } from '../src/utils/artifacts';
@@ -16,16 +17,23 @@ const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
describe('EtherToken', () => {
let account: string;
- const gasPrice = ZeroEx.toBaseUnitAmount(new BigNumber(20), 9);
- let zeroEx: ZeroEx;
+ const gasPrice = Web3Wrapper.toBaseUnitAmount(new BigNumber(20), 9);
+ let contractWrappers: ContractWrappers;
let etherTokenAddress: string;
+
+ before(async () => {
+ await blockchainLifecycle.startAsync();
+ });
+ after(async () => {
+ await blockchainLifecycle.revertAsync();
+ });
before(async () => {
const accounts = await web3Wrapper.getAvailableAddressesAsync();
account = accounts[0];
const etherToken = await WETH9Contract.deployFrom0xArtifactAsync(artifacts.EtherToken, provider, txDefaults);
etherTokenAddress = etherToken.address;
- zeroEx = new ZeroEx(provider, {
+ contractWrappers = new ContractWrappers(provider, {
gasPrice,
networkId: constants.TESTRPC_NETWORK_ID,
});
@@ -41,23 +49,26 @@ describe('EtherToken', () => {
const initEthBalance = await web3Wrapper.getBalanceInWeiAsync(account);
const ethToDeposit = initEthBalance.plus(1);
- return expect(zeroEx.etherToken.depositAsync(etherTokenAddress, ethToDeposit, account)).to.be.rejectedWith(
- ContractWrappersError.InsufficientEthBalanceForDeposit,
- );
+ return expect(
+ contractWrappers.etherToken.depositAsync(etherTokenAddress, ethToDeposit, account),
+ ).to.be.rejectedWith(ContractWrappersError.InsufficientEthBalanceForDeposit);
});
it('should convert deposited Ether to wrapped Ether tokens', async () => {
const initEthBalance = await web3Wrapper.getBalanceInWeiAsync(account);
- const initEthTokenBalance = await zeroEx.token.getBalanceAsync(etherTokenAddress, account);
+ const initEthTokenBalance = await contractWrappers.token.getBalanceAsync(etherTokenAddress, account);
const ethToDeposit = new BigNumber(Web3Wrapper.toWei(new BigNumber(1)));
- const txHash = await zeroEx.etherToken.depositAsync(etherTokenAddress, ethToDeposit, account);
- const receipt = await zeroEx.awaitTransactionMinedAsync(txHash);
+ const txHash = await contractWrappers.etherToken.depositAsync(etherTokenAddress, ethToDeposit, account);
+ const receipt = await web3Wrapper.awaitTransactionSuccessAsync(
+ txHash,
+ constants.AWAIT_TRANSACTION_MINED_MS,
+ );
const ethSpentOnGas = gasPrice.times(receipt.gasUsed);
const finalEthBalance = await web3Wrapper.getBalanceInWeiAsync(account);
- const finalEthTokenBalance = await zeroEx.token.getBalanceAsync(etherTokenAddress, account);
+ const finalEthTokenBalance = await contractWrappers.token.getBalanceAsync(etherTokenAddress, account);
expect(finalEthBalance).to.be.bignumber.equal(initEthBalance.minus(ethToDeposit.plus(ethSpentOnGas)));
expect(finalEthTokenBalance).to.be.bignumber.equal(initEthTokenBalance.plus(ethToDeposit));
@@ -66,29 +77,37 @@ describe('EtherToken', () => {
describe('withdraw', () => {
it('should throw if caller attempts to withdraw greater than caller balance', async () => {
- const initEthTokenBalance = await zeroEx.token.getBalanceAsync(etherTokenAddress, account);
+ const initEthTokenBalance = await contractWrappers.token.getBalanceAsync(etherTokenAddress, account);
const ethTokensToWithdraw = initEthTokenBalance.plus(1);
return expect(
- zeroEx.etherToken.withdrawAsync(etherTokenAddress, ethTokensToWithdraw, account),
+ contractWrappers.etherToken.withdrawAsync(etherTokenAddress, ethTokensToWithdraw, account),
).to.be.rejectedWith(ContractWrappersError.InsufficientWEthBalanceForWithdrawal);
});
it('should convert ether tokens to ether with sufficient balance', async () => {
const ethToDeposit = new BigNumber(Web3Wrapper.toWei(new BigNumber(1)));
- await zeroEx.etherToken.depositAsync(etherTokenAddress, ethToDeposit, account);
- const initEthTokenBalance = await zeroEx.token.getBalanceAsync(etherTokenAddress, account);
+ await contractWrappers.etherToken.depositAsync(etherTokenAddress, ethToDeposit, account);
+ const initEthTokenBalance = await contractWrappers.token.getBalanceAsync(etherTokenAddress, account);
const initEthBalance = await web3Wrapper.getBalanceInWeiAsync(account);
const ethTokensToWithdraw = initEthTokenBalance;
expect(ethTokensToWithdraw).to.not.be.bignumber.equal(0);
- const txHash = await zeroEx.etherToken.withdrawAsync(etherTokenAddress, ethTokensToWithdraw, account, {
- gasLimit: constants.MAX_ETHERTOKEN_WITHDRAW_GAS,
- });
- const receipt = await zeroEx.awaitTransactionMinedAsync(txHash);
+ const txHash = await contractWrappers.etherToken.withdrawAsync(
+ etherTokenAddress,
+ ethTokensToWithdraw,
+ account,
+ {
+ gasLimit: constants.MAX_ETHERTOKEN_WITHDRAW_GAS,
+ },
+ );
+ const receipt = await web3Wrapper.awaitTransactionSuccessAsync(
+ txHash,
+ constants.AWAIT_TRANSACTION_MINED_MS,
+ );
const ethSpentOnGas = gasPrice.times(receipt.gasUsed);
const finalEthBalance = await web3Wrapper.getBalanceInWeiAsync(account);
- const finalEthTokenBalance = await zeroEx.token.getBalanceAsync(etherTokenAddress, account);
+ const finalEthTokenBalance = await contractWrappers.token.getBalanceAsync(etherTokenAddress, account);
expect(finalEthBalance).to.be.bignumber.equal(
initEthBalance.plus(ethTokensToWithdraw.minus(ethSpentOnGas)),
@@ -100,9 +119,9 @@ describe('EtherToken', () => {
describe('fallback', () => {
it('should convert sent ether to ether tokens', async () => {
const initEthBalance = await web3Wrapper.getBalanceInWeiAsync(account);
- const initEthTokenBalance = await zeroEx.token.getBalanceAsync(etherTokenAddress, account);
+ const initEthTokenBalance = await contractWrappers.token.getBalanceAsync(etherTokenAddress, account);
- const ethToDeposit = ZeroEx.toBaseUnitAmount(new BigNumber(1), 18);
+ const ethToDeposit = Web3Wrapper.toBaseUnitAmount(new BigNumber(1), 18);
const txHash = await web3Wrapper.sendTransactionAsync({
from: account,
@@ -111,11 +130,14 @@ describe('EtherToken', () => {
gasPrice,
});
- const receipt = await zeroEx.awaitTransactionMinedAsync(txHash);
+ const receipt = await web3Wrapper.awaitTransactionSuccessAsync(
+ txHash,
+ constants.AWAIT_TRANSACTION_MINED_MS,
+ );
const ethSpentOnGas = gasPrice.times(receipt.gasUsed);
const finalEthBalance = await web3Wrapper.getBalanceInWeiAsync(account);
- const finalEthTokenBalance = await zeroEx.token.getBalanceAsync(etherTokenAddress, account);
+ const finalEthTokenBalance = await contractWrappers.token.getBalanceAsync(etherTokenAddress, account);
expect(finalEthBalance).to.be.bignumber.equal(initEthBalance.minus(ethToDeposit.plus(ethSpentOnGas)));
expect(finalEthTokenBalance).to.be.bignumber.equal(initEthTokenBalance.plus(ethToDeposit));
diff --git a/packages/contracts/test/exchange/core.ts b/packages/contracts/test/exchange/core.ts
index 6c190d4da..4776216ca 100644
--- a/packages/contracts/test/exchange/core.ts
+++ b/packages/contracts/test/exchange/core.ts
@@ -1,9 +1,10 @@
-import { LogWithDecodedArgs, ZeroEx } from '0x.js';
import { BlockchainLifecycle } from '@0xproject/dev-utils';
+import { LogWithDecodedArgs } from '@0xproject/types';
import { BigNumber } from '@0xproject/utils';
+import { Web3Wrapper } from '@0xproject/web3-wrapper';
import * as chai from 'chai';
import ethUtil = require('ethereumjs-util');
-import * as _ from 'lodash';
+import 'make-promises-safe';
import { DummyERC20TokenContract } from '../../src/contract_wrappers/generated/dummy_e_r_c20_token';
import { DummyERC721TokenContract } from '../../src/contract_wrappers/generated/dummy_e_r_c721_token';
@@ -12,7 +13,7 @@ import { ERC721ProxyContract } from '../../src/contract_wrappers/generated/e_r_c
import {
CancelContractEventArgs,
ExchangeContract,
- ExchangeErrorContractEventArgs,
+ ExchangeStatusContractEventArgs,
FillContractEventArgs,
} from '../../src/contract_wrappers/generated/exchange';
import { artifacts } from '../../src/utils/artifacts';
@@ -25,7 +26,7 @@ import { ERC721Wrapper } from '../../src/utils/erc721_wrapper';
import { ExchangeWrapper } from '../../src/utils/exchange_wrapper';
import { OrderFactory } from '../../src/utils/order_factory';
import { orderUtils } from '../../src/utils/order_utils';
-import { AssetProxyId, ERC20BalancesByOwner, ExchangeContractErrs, SignedOrder } from '../../src/utils/types';
+import { AssetProxyId, ContractName, ERC20BalancesByOwner, ExchangeStatus, SignedOrder } from '../../src/utils/types';
import { provider, txDefaults, web3Wrapper } from '../../src/utils/web3_wrapper';
chaiSetup.configure();
@@ -59,8 +60,12 @@ describe('Exchange core', () => {
let defaultMakerAssetAddress: string;
let defaultTakerAssetAddress: string;
- let zeroEx: ZeroEx;
-
+ before(async () => {
+ await blockchainLifecycle.startAsync();
+ });
+ after(async () => {
+ await blockchainLifecycle.revertAsync();
+ });
before(async () => {
const accounts = await web3Wrapper.getAvailableAddressesAsync();
const usedAddresses = ([owner, makerAddress, takerAddress, feeRecipientAddress] = accounts);
@@ -85,20 +90,22 @@ describe('Exchange core', () => {
txDefaults,
assetProxyUtils.encodeERC20ProxyData(zrxToken.address),
);
- zeroEx = new ZeroEx(provider, {
- exchangeContractAddress: exchange.address,
- networkId: constants.TESTRPC_NETWORK_ID,
- });
- exchangeWrapper = new ExchangeWrapper(exchange, zeroEx);
+ exchangeWrapper = new ExchangeWrapper(exchange, provider);
await exchangeWrapper.registerAssetProxyAsync(AssetProxyId.ERC20, erc20Proxy.address, owner);
await exchangeWrapper.registerAssetProxyAsync(AssetProxyId.ERC721, erc721Proxy.address, owner);
- await erc20Proxy.addAuthorizedAddress.sendTransactionAsync(exchange.address, {
- from: owner,
- });
- await erc721Proxy.addAuthorizedAddress.sendTransactionAsync(exchange.address, {
- from: 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,
+ );
defaultMakerAssetAddress = erc20TokenA.address;
defaultTakerAssetAddress = erc20TokenB.address;
@@ -166,8 +173,8 @@ describe('Exchange core', () => {
it('should transfer the correct amounts when makerAssetAmount === takerAssetAmount', async () => {
signedOrder = orderFactory.newSignedOrder({
- makerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(100), 18),
- takerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(100), 18),
+ makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18),
+ takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18),
});
const takerAssetFilledAmountBefore = await exchangeWrapper.getTakerAssetFilledAmountAsync(
@@ -219,8 +226,8 @@ describe('Exchange core', () => {
it('should transfer the correct amounts when makerAssetAmount > takerAssetAmount', async () => {
signedOrder = orderFactory.newSignedOrder({
- makerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(200), 18),
- takerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(100), 18),
+ makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(200), 18),
+ takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18),
});
const takerAssetFilledAmountBefore = await exchangeWrapper.getTakerAssetFilledAmountAsync(
@@ -272,8 +279,8 @@ describe('Exchange core', () => {
it('should transfer the correct amounts when makerAssetAmount < takerAssetAmount', async () => {
signedOrder = orderFactory.newSignedOrder({
- makerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(100), 18),
- takerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(200), 18),
+ makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18),
+ takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(200), 18),
});
const takerAssetFilledAmountBefore = await exchangeWrapper.getTakerAssetFilledAmountAsync(
@@ -326,8 +333,8 @@ describe('Exchange core', () => {
it('should transfer the correct amounts when taker is specified and order is claimed by taker', async () => {
signedOrder = orderFactory.newSignedOrder({
takerAddress,
- makerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(100), 18),
- takerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(200), 18),
+ makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18),
+ takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(200), 18),
});
const takerAssetFilledAmountBefore = await exchangeWrapper.getTakerAssetFilledAmountAsync(
@@ -445,8 +452,8 @@ describe('Exchange core', () => {
it('should throw when taker is specified and order is claimed by other', async () => {
signedOrder = orderFactory.newSignedOrder({
takerAddress: feeRecipientAddress,
- makerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(100), 18),
- takerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(200), 18),
+ makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18),
+ takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(200), 18),
});
return expect(exchangeWrapper.fillOrderAsync(signedOrder, takerAddress)).to.be.rejectedWith(
constants.REVERT,
@@ -455,7 +462,7 @@ describe('Exchange core', () => {
it('should throw if signature is invalid', async () => {
signedOrder = orderFactory.newSignedOrder({
- makerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(10), 18),
+ makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
});
const invalidR = ethUtil.sha3('invalidR');
@@ -501,7 +508,7 @@ describe('Exchange core', () => {
it('should throw if maker erc20Balances are too low to fill order', async () => {
signedOrder = orderFactory.newSignedOrder({
- makerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(100000), 18),
+ makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100000), 18),
});
return expect(exchangeWrapper.fillOrderAsync(signedOrder, takerAddress)).to.be.rejectedWith(
@@ -511,7 +518,7 @@ describe('Exchange core', () => {
it('should throw if taker erc20Balances are too low to fill order', async () => {
signedOrder = orderFactory.newSignedOrder({
- takerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(100000), 18),
+ takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100000), 18),
});
return expect(exchangeWrapper.fillOrderAsync(signedOrder, takerAddress)).to.be.rejectedWith(
@@ -520,23 +527,45 @@ describe('Exchange core', () => {
});
it('should throw if maker allowances are too low to fill order', async () => {
- await erc20TokenA.approve.sendTransactionAsync(erc20Proxy.address, new BigNumber(0), {
- from: makerAddress,
- });
- expect(exchangeWrapper.fillOrderAsync(signedOrder, takerAddress)).to.be.rejectedWith(constants.REVERT);
- await erc20TokenA.approve.sendTransactionAsync(erc20Proxy.address, constants.INITIAL_ERC20_ALLOWANCE, {
- from: makerAddress,
- });
+ await web3Wrapper.awaitTransactionSuccessAsync(
+ await erc20TokenA.approve.sendTransactionAsync(erc20Proxy.address, new BigNumber(0), {
+ from: makerAddress,
+ }),
+ constants.AWAIT_TRANSACTION_MINED_MS,
+ );
+ // HACK: `rejectWith` returns a "promise-like" type, but not an actual "Promise", so TSLint
+ // complains, even though we do need to `await` it. So we disable the TSLint error below.
+ // tslint:disable-next-line:await-promise
+ await expect(exchangeWrapper.fillOrderAsync(signedOrder, takerAddress)).to.be.rejectedWith(
+ constants.REVERT,
+ );
+ await web3Wrapper.awaitTransactionSuccessAsync(
+ await erc20TokenA.approve.sendTransactionAsync(erc20Proxy.address, constants.INITIAL_ERC20_ALLOWANCE, {
+ from: makerAddress,
+ }),
+ constants.AWAIT_TRANSACTION_MINED_MS,
+ );
});
it('should throw if taker allowances are too low to fill order', async () => {
- await erc20TokenB.approve.sendTransactionAsync(erc20Proxy.address, new BigNumber(0), {
- from: takerAddress,
- });
- expect(exchangeWrapper.fillOrderAsync(signedOrder, takerAddress)).to.be.rejectedWith(constants.REVERT);
- await erc20TokenB.approve.sendTransactionAsync(erc20Proxy.address, constants.INITIAL_ERC20_ALLOWANCE, {
- from: takerAddress,
- });
+ await web3Wrapper.awaitTransactionSuccessAsync(
+ await erc20TokenB.approve.sendTransactionAsync(erc20Proxy.address, new BigNumber(0), {
+ from: takerAddress,
+ }),
+ constants.AWAIT_TRANSACTION_MINED_MS,
+ );
+ // HACK: `rejectWith` returns a "promise-like" type, but not an actual "Promise", so TSLint
+ // complains, even though we do need to `await` it. So we disable the TSLint error below.
+ // tslint:disable-next-line:await-promise
+ await expect(exchangeWrapper.fillOrderAsync(signedOrder, takerAddress)).to.be.rejectedWith(
+ constants.REVERT,
+ );
+ await web3Wrapper.awaitTransactionSuccessAsync(
+ await erc20TokenB.approve.sendTransactionAsync(erc20Proxy.address, constants.INITIAL_ERC20_ALLOWANCE, {
+ from: takerAddress,
+ }),
+ constants.AWAIT_TRANSACTION_MINED_MS,
+ );
});
it('should not change erc20Balances if an order is expired', async () => {
@@ -556,9 +585,9 @@ describe('Exchange core', () => {
const res = await exchangeWrapper.fillOrderAsync(signedOrder, takerAddress);
expect(res.logs).to.have.length(1);
- const log = res.logs[0] as LogWithDecodedArgs<ExchangeErrorContractEventArgs>;
- const errCode = log.args.errorId;
- expect(errCode).to.be.equal(ExchangeContractErrs.ERROR_ORDER_EXPIRED);
+ const log = res.logs[0] as LogWithDecodedArgs<ExchangeStatusContractEventArgs>;
+ const statusCode = log.args.statusId;
+ expect(statusCode).to.be.equal(ExchangeStatus.ORDER_EXPIRED);
});
it('should log an error event if no value is filled', async () => {
@@ -567,9 +596,9 @@ describe('Exchange core', () => {
const res = await exchangeWrapper.fillOrderAsync(signedOrder, takerAddress);
expect(res.logs).to.have.length(1);
- const log = res.logs[0] as LogWithDecodedArgs<ExchangeErrorContractEventArgs>;
- const errCode = log.args.errorId;
- expect(errCode).to.be.equal(ExchangeContractErrs.ERROR_ORDER_FULLY_FILLED);
+ const log = res.logs[0] as LogWithDecodedArgs<ExchangeStatusContractEventArgs>;
+ const statusCode = log.args.statusId;
+ expect(statusCode).to.be.equal(ExchangeStatus.ORDER_FULLY_FILLED);
});
});
@@ -635,9 +664,9 @@ describe('Exchange core', () => {
const res = await exchangeWrapper.cancelOrderAsync(signedOrder, makerAddress);
expect(res.logs).to.have.length(1);
- const log = res.logs[0] as LogWithDecodedArgs<ExchangeErrorContractEventArgs>;
- const errCode = log.args.errorId;
- expect(errCode).to.be.equal(ExchangeContractErrs.ERROR_ORDER_CANCELLED);
+ const log = res.logs[0] as LogWithDecodedArgs<ExchangeStatusContractEventArgs>;
+ const statusCode = log.args.statusId;
+ expect(statusCode).to.be.equal(ExchangeStatus.ORDER_CANCELLED);
});
it('should log error if order is expired', async () => {
@@ -647,9 +676,9 @@ describe('Exchange core', () => {
const res = await exchangeWrapper.cancelOrderAsync(signedOrder, makerAddress);
expect(res.logs).to.have.length(1);
- const log = res.logs[0] as LogWithDecodedArgs<ExchangeErrorContractEventArgs>;
- const errCode = log.args.errorId;
- expect(errCode).to.be.equal(ExchangeContractErrs.ERROR_ORDER_EXPIRED);
+ const log = res.logs[0] as LogWithDecodedArgs<ExchangeStatusContractEventArgs>;
+ const statusCode = log.args.statusId;
+ expect(statusCode).to.be.equal(ExchangeStatus.ORDER_EXPIRED);
});
});
@@ -681,23 +710,23 @@ describe('Exchange core', () => {
erc20Balances = await erc20Wrapper.getBalancesAsync();
const signedOrders = await Promise.all([
orderFactory.newSignedOrder({
- makerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(9), 18),
- takerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(9), 18),
+ makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(9), 18),
+ takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(9), 18),
salt: new BigNumber(0),
}),
orderFactory.newSignedOrder({
- makerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(79), 18),
- takerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(79), 18),
+ makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(79), 18),
+ takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(79), 18),
salt: new BigNumber(1),
}),
orderFactory.newSignedOrder({
- makerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(979), 18),
- takerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(979), 18),
+ makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(979), 18),
+ takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(979), 18),
salt: new BigNumber(2),
}),
orderFactory.newSignedOrder({
- makerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(7979), 18),
- takerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(7979), 18),
+ makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(7979), 18),
+ takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(7979), 18),
salt: new BigNumber(3),
}),
]);
@@ -873,7 +902,7 @@ describe('Exchange core', () => {
const makerAssetId = erc721MakerAssetIds[0];
signedOrder = orderFactory.newSignedOrder({
makerAssetAmount: new BigNumber(1),
- takerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(100), 18),
+ takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18),
makerAssetData: assetProxyUtils.encodeERC721ProxyData(erc721Token.address, makerAssetId),
takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultTakerAssetAddress),
});
@@ -913,7 +942,7 @@ describe('Exchange core', () => {
const takerAssetId = erc721TakerAssetIds[0];
signedOrder = orderFactory.newSignedOrder({
takerAssetAmount: new BigNumber(1),
- makerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(100), 18),
+ makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18),
takerAssetData: assetProxyUtils.encodeERC721ProxyData(erc721Token.address, takerAssetId),
makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultMakerAssetAddress),
});
diff --git a/packages/contracts/test/exchange/dispatcher.ts b/packages/contracts/test/exchange/dispatcher.ts
index db2f18ddc..b9c7039bd 100644
--- a/packages/contracts/test/exchange/dispatcher.ts
+++ b/packages/contracts/test/exchange/dispatcher.ts
@@ -1,4 +1,3 @@
-import { ZeroEx } from '0x.js';
import { BlockchainLifecycle } from '@0xproject/dev-utils';
import { BigNumber } from '@0xproject/utils';
import * as chai from 'chai';
@@ -37,6 +36,12 @@ describe('AssetProxyDispatcher', () => {
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] = accounts);
@@ -56,12 +61,18 @@ describe('AssetProxyDispatcher', () => {
provider,
txDefaults,
);
- await erc20Proxy.addAuthorizedAddress.sendTransactionAsync(assetProxyDispatcher.address, {
- from: owner,
- });
- await erc721Proxy.addAuthorizedAddress.sendTransactionAsync(assetProxyDispatcher.address, {
- from: owner,
- });
+ 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();
@@ -71,12 +82,15 @@ describe('AssetProxyDispatcher', () => {
});
describe('registerAssetProxy', () => {
it('should record proxy upon registration', async () => {
- const prevProxyAddress = ZeroEx.NULL_ADDRESS;
- await assetProxyDispatcher.registerAssetProxy.sendTransactionAsync(
- AssetProxyId.ERC20,
- erc20Proxy.address,
- prevProxyAddress,
- { from: owner },
+ const prevProxyAddress = constants.NULL_ADDRESS;
+ await web3Wrapper.awaitTransactionSuccessAsync(
+ await assetProxyDispatcher.registerAssetProxy.sendTransactionAsync(
+ AssetProxyId.ERC20,
+ erc20Proxy.address,
+ prevProxyAddress,
+ { from: owner },
+ ),
+ constants.AWAIT_TRANSACTION_MINED_MS,
);
const proxyAddress = await assetProxyDispatcher.getAssetProxy.callAsync(AssetProxyId.ERC20);
expect(proxyAddress).to.be.equal(erc20Proxy.address);
@@ -84,22 +98,28 @@ describe('AssetProxyDispatcher', () => {
it('should be able to record multiple proxies', async () => {
// Record first proxy
- const prevERC20ProxyAddress = ZeroEx.NULL_ADDRESS;
- await assetProxyDispatcher.registerAssetProxy.sendTransactionAsync(
- AssetProxyId.ERC20,
- erc20Proxy.address,
- prevERC20ProxyAddress,
- { from: owner },
+ const prevERC20ProxyAddress = constants.NULL_ADDRESS;
+ await web3Wrapper.awaitTransactionSuccessAsync(
+ await assetProxyDispatcher.registerAssetProxy.sendTransactionAsync(
+ AssetProxyId.ERC20,
+ erc20Proxy.address,
+ prevERC20ProxyAddress,
+ { 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
- const prevERC721ProxyAddress = ZeroEx.NULL_ADDRESS;
- await assetProxyDispatcher.registerAssetProxy.sendTransactionAsync(
- AssetProxyId.ERC721,
- erc721Proxy.address,
- prevERC721ProxyAddress,
- { from: owner },
+ const prevERC721ProxyAddress = constants.NULL_ADDRESS;
+ await web3Wrapper.awaitTransactionSuccessAsync(
+ await assetProxyDispatcher.registerAssetProxy.sendTransactionAsync(
+ AssetProxyId.ERC721,
+ erc721Proxy.address,
+ prevERC721ProxyAddress,
+ { from: owner },
+ ),
+ constants.AWAIT_TRANSACTION_MINED_MS,
);
proxyAddress = await assetProxyDispatcher.getAssetProxy.callAsync(AssetProxyId.ERC721);
expect(proxyAddress).to.be.equal(erc721Proxy.address);
@@ -107,12 +127,15 @@ describe('AssetProxyDispatcher', () => {
it('should replace proxy address upon re-registration', async () => {
// Initial registration
- const prevProxyAddress = ZeroEx.NULL_ADDRESS;
- await assetProxyDispatcher.registerAssetProxy.sendTransactionAsync(
- AssetProxyId.ERC20,
- erc20Proxy.address,
- prevProxyAddress,
- { from: owner },
+ const prevProxyAddress = constants.NULL_ADDRESS;
+ await web3Wrapper.awaitTransactionSuccessAsync(
+ await assetProxyDispatcher.registerAssetProxy.sendTransactionAsync(
+ AssetProxyId.ERC20,
+ erc20Proxy.address,
+ prevProxyAddress,
+ { from: owner },
+ ),
+ constants.AWAIT_TRANSACTION_MINED_MS,
);
let proxyAddress = await assetProxyDispatcher.getAssetProxy.callAsync(AssetProxyId.ERC20);
expect(proxyAddress).to.be.equal(erc20Proxy.address);
@@ -125,11 +148,14 @@ describe('AssetProxyDispatcher', () => {
// Register new ERC20 Transfer Proxy contract
const newAddress = newErc20TransferProxy.address;
const currentAddress = erc20Proxy.address;
- await assetProxyDispatcher.registerAssetProxy.sendTransactionAsync(
- AssetProxyId.ERC20,
- newAddress,
- currentAddress,
- { from: owner },
+ await web3Wrapper.awaitTransactionSuccessAsync(
+ await assetProxyDispatcher.registerAssetProxy.sendTransactionAsync(
+ AssetProxyId.ERC20,
+ newAddress,
+ currentAddress,
+ { from: owner },
+ ),
+ constants.AWAIT_TRANSACTION_MINED_MS,
);
// Verify new asset proxy has replaced initial version
proxyAddress = await assetProxyDispatcher.getAssetProxy.callAsync(AssetProxyId.ERC20);
@@ -138,21 +164,24 @@ describe('AssetProxyDispatcher', () => {
it('should throw if registering with incorrect "currentAssetProxyAddress" field', async () => {
// Initial registration
- const prevProxyAddress = ZeroEx.NULL_ADDRESS;
- await assetProxyDispatcher.registerAssetProxy.sendTransactionAsync(
- AssetProxyId.ERC20,
- erc20Proxy.address,
- prevProxyAddress,
- { from: owner },
+ const prevProxyAddress = constants.NULL_ADDRESS;
+ await web3Wrapper.awaitTransactionSuccessAsync(
+ await assetProxyDispatcher.registerAssetProxy.sendTransactionAsync(
+ AssetProxyId.ERC20,
+ erc20Proxy.address,
+ prevProxyAddress,
+ { from: owner },
+ ),
+ constants.AWAIT_TRANSACTION_MINED_MS,
);
const proxyAddress = await assetProxyDispatcher.getAssetProxy.callAsync(AssetProxyId.ERC20);
expect(proxyAddress).to.be.equal(erc20Proxy.address);
- // The following transaction will throw because the currentAddress is no longer ZeroEx.NULL_ADDRESS
+ // The following transaction will throw because the currentAddress is no longer constants.NULL_ADDRESS
return expect(
assetProxyDispatcher.registerAssetProxy.sendTransactionAsync(
AssetProxyId.ERC20,
erc20Proxy.address,
- ZeroEx.NULL_ADDRESS,
+ constants.NULL_ADDRESS,
{ from: owner },
),
).to.be.rejectedWith(constants.REVERT);
@@ -160,29 +189,35 @@ describe('AssetProxyDispatcher', () => {
it('should be able to reset proxy address to NULL', async () => {
// Initial registration
- const prevProxyAddress = ZeroEx.NULL_ADDRESS;
- await assetProxyDispatcher.registerAssetProxy.sendTransactionAsync(
- AssetProxyId.ERC20,
- erc20Proxy.address,
- prevProxyAddress,
- { from: owner },
+ const prevProxyAddress = constants.NULL_ADDRESS;
+ await web3Wrapper.awaitTransactionSuccessAsync(
+ await assetProxyDispatcher.registerAssetProxy.sendTransactionAsync(
+ AssetProxyId.ERC20,
+ erc20Proxy.address,
+ prevProxyAddress,
+ { from: owner },
+ ),
+ constants.AWAIT_TRANSACTION_MINED_MS,
);
const proxyAddress = await assetProxyDispatcher.getAssetProxy.callAsync(AssetProxyId.ERC20);
expect(proxyAddress).to.be.equal(erc20Proxy.address);
// The following transaction will reset the proxy address
- const newProxyAddress = ZeroEx.NULL_ADDRESS;
- await assetProxyDispatcher.registerAssetProxy.sendTransactionAsync(
- AssetProxyId.ERC20,
- newProxyAddress,
- erc20Proxy.address,
- { from: owner },
+ const newProxyAddress = constants.NULL_ADDRESS;
+ await web3Wrapper.awaitTransactionSuccessAsync(
+ await assetProxyDispatcher.registerAssetProxy.sendTransactionAsync(
+ AssetProxyId.ERC20,
+ newProxyAddress,
+ erc20Proxy.address,
+ { from: owner },
+ ),
+ constants.AWAIT_TRANSACTION_MINED_MS,
);
const finalProxyAddress = await assetProxyDispatcher.getAssetProxy.callAsync(AssetProxyId.ERC20);
expect(finalProxyAddress).to.be.equal(newProxyAddress);
});
it('should throw if requesting address is not owner', async () => {
- const prevProxyAddress = ZeroEx.NULL_ADDRESS;
+ const prevProxyAddress = constants.NULL_ADDRESS;
return expect(
assetProxyDispatcher.registerAssetProxy.sendTransactionAsync(
AssetProxyId.ERC20,
@@ -194,7 +229,7 @@ describe('AssetProxyDispatcher', () => {
});
it('should throw if attempting to register a proxy to the incorrect id', async () => {
- const prevProxyAddress = ZeroEx.NULL_ADDRESS;
+ const prevProxyAddress = constants.NULL_ADDRESS;
return expect(
assetProxyDispatcher.registerAssetProxy.sendTransactionAsync(
AssetProxyId.ERC721,
@@ -208,12 +243,15 @@ describe('AssetProxyDispatcher', () => {
describe('getAssetProxy', () => {
it('should return correct address of registered proxy', async () => {
- const prevProxyAddress = ZeroEx.NULL_ADDRESS;
- await assetProxyDispatcher.registerAssetProxy.sendTransactionAsync(
- AssetProxyId.ERC20,
- erc20Proxy.address,
- prevProxyAddress,
- { from: owner },
+ const prevProxyAddress = constants.NULL_ADDRESS;
+ await web3Wrapper.awaitTransactionSuccessAsync(
+ await assetProxyDispatcher.registerAssetProxy.sendTransactionAsync(
+ AssetProxyId.ERC20,
+ erc20Proxy.address,
+ prevProxyAddress,
+ { from: owner },
+ ),
+ constants.AWAIT_TRANSACTION_MINED_MS,
);
const proxyAddress = await assetProxyDispatcher.getAssetProxy.callAsync(AssetProxyId.ERC20);
expect(proxyAddress).to.be.equal(erc20Proxy.address);
@@ -221,31 +259,37 @@ describe('AssetProxyDispatcher', () => {
it('should return NULL address if requesting non-existent proxy', async () => {
const proxyAddress = await assetProxyDispatcher.getAssetProxy.callAsync(AssetProxyId.ERC20);
- expect(proxyAddress).to.be.equal(ZeroEx.NULL_ADDRESS);
+ expect(proxyAddress).to.be.equal(constants.NULL_ADDRESS);
});
});
describe('dispatchTransferFrom', () => {
it('should dispatch transfer to registered proxy', async () => {
// Register ERC20 proxy
- const prevProxyAddress = ZeroEx.NULL_ADDRESS;
- await assetProxyDispatcher.registerAssetProxy.sendTransactionAsync(
- AssetProxyId.ERC20,
- erc20Proxy.address,
- prevProxyAddress,
- { from: owner },
+ const prevProxyAddress = constants.NULL_ADDRESS;
+ await web3Wrapper.awaitTransactionSuccessAsync(
+ await assetProxyDispatcher.registerAssetProxy.sendTransactionAsync(
+ AssetProxyId.ERC20,
+ erc20Proxy.address,
+ prevProxyAddress,
+ { from: owner },
+ ),
+ constants.AWAIT_TRANSACTION_MINED_MS,
);
// Construct metadata for ERC20 proxy
const encodedProxyMetadata = assetProxyUtils.encodeERC20ProxyData(zrxToken.address);
// Perform a transfer from makerAddress to takerAddress
const erc20Balances = await erc20Wrapper.getBalancesAsync();
const amount = new BigNumber(10);
- await assetProxyDispatcher.publicDispatchTransferFrom.sendTransactionAsync(
- encodedProxyMetadata,
- makerAddress,
- takerAddress,
- amount,
- { from: owner },
+ await web3Wrapper.awaitTransactionSuccessAsync(
+ await assetProxyDispatcher.publicDispatchTransferFrom.sendTransactionAsync(
+ encodedProxyMetadata,
+ makerAddress,
+ takerAddress,
+ amount,
+ { from: owner },
+ ),
+ constants.AWAIT_TRANSACTION_MINED_MS,
);
// Verify transfer was successful
const newBalances = await erc20Wrapper.getBalancesAsync();
diff --git a/packages/contracts/test/exchange/libs.ts b/packages/contracts/test/exchange/libs.ts
index 1036cb815..5c530a9b1 100644
--- a/packages/contracts/test/exchange/libs.ts
+++ b/packages/contracts/test/exchange/libs.ts
@@ -1,4 +1,3 @@
-import { ZeroEx } from '0x.js';
import { BlockchainLifecycle } from '@0xproject/dev-utils';
import { BigNumber } from '@0xproject/utils';
import * as chai from 'chai';
@@ -26,10 +25,15 @@ describe('Exchange libs', () => {
let libs: TestLibsContract;
before(async () => {
+ await blockchainLifecycle.startAsync();
+ });
+ after(async () => {
+ await blockchainLifecycle.revertAsync();
+ });
+ before(async () => {
const accounts = await web3Wrapper.getAvailableAddressesAsync();
const makerAddress = accounts[0];
libs = await TestLibsContract.deployFrom0xArtifactAsync(artifacts.TestLibs, provider, txDefaults);
- const zeroEx = new ZeroEx(provider, { networkId: constants.TESTRPC_NETWORK_ID });
const defaultOrderParams = {
...constants.STATIC_ORDER_PARAMS,
diff --git a/packages/contracts/test/exchange/match_orders.ts b/packages/contracts/test/exchange/match_orders.ts
new file mode 100644
index 000000000..1834e929c
--- /dev/null
+++ b/packages/contracts/test/exchange/match_orders.ts
@@ -0,0 +1,838 @@
+import { BlockchainLifecycle } from '@0xproject/dev-utils';
+import { LogWithDecodedArgs } from '@0xproject/types';
+import { BigNumber } from '@0xproject/utils';
+import { Web3Wrapper } from '@0xproject/web3-wrapper';
+import * as chai from 'chai';
+import ethUtil = require('ethereumjs-util');
+import * as _ from 'lodash';
+
+import { DummyERC20TokenContract } from '../../src/contract_wrappers/generated/dummy_e_r_c20_token';
+import { DummyERC721TokenContract } from '../../src/contract_wrappers/generated/dummy_e_r_c721_token';
+import { ERC20ProxyContract } from '../../src/contract_wrappers/generated/e_r_c20_proxy';
+import { ERC721ProxyContract } from '../../src/contract_wrappers/generated/e_r_c721_proxy';
+import {
+ CancelContractEventArgs,
+ ExchangeContract,
+ ExchangeStatusContractEventArgs,
+ FillContractEventArgs,
+} from '../../src/contract_wrappers/generated/exchange';
+import { artifacts } from '../../src/utils/artifacts';
+import { assetProxyUtils } from '../../src/utils/asset_proxy_utils';
+import { chaiSetup } from '../../src/utils/chai_setup';
+import { constants } from '../../src/utils/constants';
+import { crypto } from '../../src/utils/crypto';
+import { ERC20Wrapper } from '../../src/utils/erc20_wrapper';
+import { ERC721Wrapper } from '../../src/utils/erc721_wrapper';
+import { ExchangeWrapper } from '../../src/utils/exchange_wrapper';
+import { OrderFactory } from '../../src/utils/order_factory';
+import { orderUtils } from '../../src/utils/order_utils';
+import {
+ AssetProxyId,
+ ContractName,
+ ERC20BalancesByOwner,
+ ERC721TokenIdsByOwner,
+ ExchangeStatus,
+ OrderInfo,
+ SignedOrder,
+} from '../../src/utils/types';
+import { provider, txDefaults, web3Wrapper } from '../../src/utils/web3_wrapper';
+
+import { MatchOrderTester } from '../../src/utils/match_order_tester';
+
+chaiSetup.configure();
+const expect = chai.expect;
+const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
+
+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 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 erc721TakerAssetIds: BigNumber[];
+
+ let defaultERC20MakerAssetAddress: string;
+ let defaultERC20TakerAssetAddress: string;
+ let defaultERC721AssetAddress: string;
+
+ let matchOrderTester: MatchOrderTester;
+
+ before(async () => {
+ await blockchainLifecycle.startAsync();
+ });
+ after(async () => {
+ await blockchainLifecycle.revertAsync();
+ });
+ before(async () => {
+ // Create accounts
+ const accounts = await web3Wrapper.getAvailableAddressesAsync();
+ const usedAddresses = ([
+ owner,
+ makerAddressLeft,
+ makerAddressRight,
+ takerAddress,
+ feeRecipientAddressLeft,
+ feeRecipientAddressRight,
+ ] = accounts);
+ // Create wrappers
+ erc20Wrapper = new ERC20Wrapper(provider, usedAddresses, owner);
+ erc721Wrapper = new ERC721Wrapper(provider, usedAddresses, owner);
+ // Deploy ERC20 token & ERC20 proxy
+ [erc20TokenA, erc20TokenB, zrxToken] = await erc20Wrapper.deployDummyTokensAsync();
+ 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];
+ erc721TakerAssetIds = erc721Balances[takerAddress][erc721Token.address];
+ // Depoy exchange
+ exchange = await ExchangeContract.deployFrom0xArtifactAsync(
+ artifacts.Exchange,
+ provider,
+ txDefaults,
+ assetProxyUtils.encodeERC20ProxyData(zrxToken.address),
+ );
+ exchangeWrapper = new ExchangeWrapper(exchange, provider);
+ await exchangeWrapper.registerAssetProxyAsync(AssetProxyId.ERC20, erc20Proxy.address, owner);
+ await exchangeWrapper.registerAssetProxyAsync(AssetProxyId.ERC721, 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,
+ );
+ // Set default addresses
+ defaultERC20MakerAssetAddress = erc20TokenA.address;
+ defaultERC20TakerAssetAddress = erc20TokenB.address;
+ defaultERC721AssetAddress = erc721Token.address;
+ // Create default order parameters
+ const defaultOrderParams = {
+ ...constants.STATIC_ORDER_PARAMS,
+ exchangeAddress: exchange.address,
+ makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
+ takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
+ };
+ const privateKeyLeft = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddressLeft)];
+ orderFactoryLeft = new OrderFactory(privateKeyLeft, defaultOrderParams);
+ const privateKeyRight = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddressRight)];
+ orderFactoryRight = new OrderFactory(privateKeyRight, defaultOrderParams);
+ // Set match order tester
+ matchOrderTester = new MatchOrderTester(exchangeWrapper, erc20Wrapper, erc721Wrapper, zrxToken.address);
+ });
+ 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 the correct amounts when orders completely fill each other', async () => {
+ // Create orders to match
+ const signedOrderLeft = orderFactoryLeft.newSignedOrder({
+ makerAddress: makerAddressLeft,
+ makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
+ takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
+ makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18),
+ takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
+ feeRecipientAddress: feeRecipientAddressLeft,
+ });
+ const signedOrderRight = orderFactoryRight.newSignedOrder({
+ makerAddress: makerAddressRight,
+ makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
+ takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
+ makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
+ takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18),
+ feeRecipientAddress: feeRecipientAddressRight,
+ });
+ // Match signedOrderLeft with signedOrderRight
+ await matchOrderTester.matchOrdersAndVerifyBalancesAsync(
+ signedOrderLeft,
+ signedOrderRight,
+ takerAddress,
+ erc20BalancesByOwner,
+ erc721TokenIdsByOwner,
+ );
+ // Verify left order was fully filled
+ const leftOrderInfo: OrderInfo = await exchangeWrapper.getOrderInfoAsync(signedOrderLeft);
+ expect(leftOrderInfo.orderStatus as ExchangeStatus).to.be.equal(ExchangeStatus.ORDER_FULLY_FILLED);
+ // Verify right order was fully filled
+ const rightOrderInfo: OrderInfo = await exchangeWrapper.getOrderInfoAsync(signedOrderRight);
+ expect(rightOrderInfo.orderStatus as ExchangeStatus).to.be.equal(ExchangeStatus.ORDER_FULLY_FILLED);
+ });
+
+ 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 = orderFactoryLeft.newSignedOrder({
+ makerAddress: makerAddressLeft,
+ makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
+ takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
+ makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18),
+ takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
+ feeRecipientAddress: feeRecipientAddressLeft,
+ });
+ const signedOrderRight = orderFactoryRight.newSignedOrder({
+ makerAddress: makerAddressRight,
+ makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
+ takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
+ makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
+ takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18),
+ feeRecipientAddress: feeRecipientAddressRight,
+ });
+ // Store original taker balance
+ const takerInitialBalances = _.cloneDeep(erc20BalancesByOwner[takerAddress][defaultERC20MakerAssetAddress]);
+ // Match signedOrderLeft with signedOrderRight
+ let newERC20BalancesByOwner: ERC20BalancesByOwner;
+ let newERC721TokenIdsByOwner: ERC721TokenIdsByOwner;
+ [
+ newERC20BalancesByOwner,
+ newERC721TokenIdsByOwner,
+ ] = await matchOrderTester.matchOrdersAndVerifyBalancesAsync(
+ signedOrderLeft,
+ signedOrderRight,
+ takerAddress,
+ erc20BalancesByOwner,
+ erc721TokenIdsByOwner,
+ );
+ // Verify left order was fully filled
+ const leftOrderInfo: OrderInfo = await exchangeWrapper.getOrderInfoAsync(signedOrderLeft);
+ expect(leftOrderInfo.orderStatus as ExchangeStatus).to.be.equal(ExchangeStatus.ORDER_FULLY_FILLED);
+ // Verify right order was fully filled
+ const rightOrderInfo: OrderInfo = await exchangeWrapper.getOrderInfoAsync(signedOrderRight);
+ expect(rightOrderInfo.orderStatus as ExchangeStatus).to.be.equal(ExchangeStatus.ORDER_FULLY_FILLED);
+ // Verify taker did not take a profit
+ expect(takerInitialBalances).to.be.deep.equal(
+ newERC20BalancesByOwner[takerAddress][defaultERC20MakerAssetAddress],
+ );
+ });
+
+ 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 = orderFactoryLeft.newSignedOrder({
+ makerAddress: makerAddressLeft,
+ makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
+ takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
+ makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18),
+ takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
+ feeRecipientAddress: feeRecipientAddressLeft,
+ });
+ const signedOrderRight = orderFactoryRight.newSignedOrder({
+ makerAddress: makerAddressRight,
+ makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
+ takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
+ makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(20), 18),
+ takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(4), 18),
+ feeRecipientAddress: feeRecipientAddressRight,
+ });
+ // Match orders
+ await matchOrderTester.matchOrdersAndVerifyBalancesAsync(
+ signedOrderLeft,
+ signedOrderRight,
+ takerAddress,
+ erc20BalancesByOwner,
+ erc721TokenIdsByOwner,
+ );
+ // Verify left order was fully filled
+ const leftOrderInfo: OrderInfo = await exchangeWrapper.getOrderInfoAsync(signedOrderLeft);
+ expect(leftOrderInfo.orderStatus as ExchangeStatus).to.be.equal(ExchangeStatus.ORDER_FULLY_FILLED);
+ // Verify right order was partially filled
+ const rightOrderInfo: OrderInfo = await exchangeWrapper.getOrderInfoAsync(signedOrderRight);
+ expect(rightOrderInfo.orderStatus as ExchangeStatus).to.be.equal(ExchangeStatus.ORDER_FILLABLE);
+ });
+
+ 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 = orderFactoryLeft.newSignedOrder({
+ makerAddress: makerAddressLeft,
+ makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
+ takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
+ makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(50), 18),
+ takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18),
+ feeRecipientAddress: feeRecipientAddressLeft,
+ });
+ const signedOrderRight = orderFactoryRight.newSignedOrder({
+ makerAddress: makerAddressRight,
+ makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
+ takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
+ makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
+ takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18),
+ feeRecipientAddress: feeRecipientAddressRight,
+ });
+ // Match orders
+ await matchOrderTester.matchOrdersAndVerifyBalancesAsync(
+ signedOrderLeft,
+ signedOrderRight,
+ takerAddress,
+ erc20BalancesByOwner,
+ erc721TokenIdsByOwner,
+ );
+ // Verify left order was partially filled
+ const leftOrderInfo: OrderInfo = await exchangeWrapper.getOrderInfoAsync(signedOrderLeft);
+ expect(leftOrderInfo.orderStatus as ExchangeStatus).to.be.equal(ExchangeStatus.ORDER_FILLABLE);
+ // Verify right order was fully filled
+ const rightOrderInfo: OrderInfo = await exchangeWrapper.getOrderInfoAsync(signedOrderRight);
+ expect(rightOrderInfo.orderStatus as ExchangeStatus).to.be.equal(ExchangeStatus.ORDER_FULLY_FILLED);
+ });
+
+ it('should transfer the correct amounts when consecutive calls are used to completely fill the left order', async () => {
+ // Create orders to match
+ const signedOrderLeft = orderFactoryLeft.newSignedOrder({
+ makerAddress: makerAddressLeft,
+ makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
+ takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
+ makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(50), 18),
+ takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18),
+ feeRecipientAddress: feeRecipientAddressLeft,
+ });
+ const signedOrderRight = orderFactoryRight.newSignedOrder({
+ makerAddress: makerAddressRight,
+ makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
+ takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
+ makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
+ takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18),
+ feeRecipientAddress: feeRecipientAddressRight,
+ });
+ // Match orders
+ let newERC20BalancesByOwner: ERC20BalancesByOwner;
+ let newERC721TokenIdsByOwner: ERC721TokenIdsByOwner;
+ [
+ newERC20BalancesByOwner,
+ newERC721TokenIdsByOwner,
+ ] = await matchOrderTester.matchOrdersAndVerifyBalancesAsync(
+ signedOrderLeft,
+ signedOrderRight,
+ takerAddress,
+ erc20BalancesByOwner,
+ erc721TokenIdsByOwner,
+ );
+ // Verify left order was partially filled
+ const leftOrderInfo: OrderInfo = await exchangeWrapper.getOrderInfoAsync(signedOrderLeft);
+ expect(leftOrderInfo.orderStatus as ExchangeStatus).to.be.equal(ExchangeStatus.ORDER_FILLABLE);
+ // Verify right order was fully filled
+ const rightOrderInfo: OrderInfo = await exchangeWrapper.getOrderInfoAsync(signedOrderRight);
+ expect(rightOrderInfo.orderStatus as ExchangeStatus).to.be.equal(ExchangeStatus.ORDER_FULLY_FILLED);
+ // 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 = orderFactoryRight.newSignedOrder({
+ makerAddress: makerAddressRight,
+ makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
+ takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
+ makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18),
+ takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(50), 18),
+ feeRecipientAddress: feeRecipientAddressRight,
+ });
+ // Match signedOrderLeft with signedOrderRight2
+ const leftTakerAssetFilledAmount = signedOrderRight.makerAssetAmount;
+ const rightTakerAssetFilledAmount = new BigNumber(0);
+ await matchOrderTester.matchOrdersAndVerifyBalancesAsync(
+ signedOrderLeft,
+ signedOrderRight2,
+ takerAddress,
+ newERC20BalancesByOwner,
+ erc721TokenIdsByOwner,
+ leftTakerAssetFilledAmount,
+ rightTakerAssetFilledAmount,
+ );
+ // Verify left order was fully filled
+ const leftOrderInfo2: OrderInfo = await exchangeWrapper.getOrderInfoAsync(signedOrderLeft);
+ expect(leftOrderInfo2.orderStatus as ExchangeStatus).to.be.equal(ExchangeStatus.ORDER_FULLY_FILLED);
+ // Verify second right order was partially filled
+ const rightOrderInfo2: OrderInfo = await exchangeWrapper.getOrderInfoAsync(signedOrderRight2);
+ expect(rightOrderInfo2.orderStatus as ExchangeStatus).to.be.equal(ExchangeStatus.ORDER_FILLABLE);
+ });
+
+ it('should transfer the correct amounts when consecutive calls are used to completely fill the right order', async () => {
+ // Create orders to match
+ const signedOrderLeft = orderFactoryLeft.newSignedOrder({
+ makerAddress: makerAddressLeft,
+ makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
+ takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
+ makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
+ takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18),
+ feeRecipientAddress: feeRecipientAddressLeft,
+ });
+
+ const signedOrderRight = orderFactoryRight.newSignedOrder({
+ makerAddress: makerAddressRight,
+ makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
+ takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
+ makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(50), 18),
+ takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18),
+ feeRecipientAddress: feeRecipientAddressRight,
+ });
+ // Match orders
+ let newERC20BalancesByOwner: ERC20BalancesByOwner;
+ let newERC721TokenIdsByOwner: ERC721TokenIdsByOwner;
+ [
+ newERC20BalancesByOwner,
+ newERC721TokenIdsByOwner,
+ ] = await matchOrderTester.matchOrdersAndVerifyBalancesAsync(
+ signedOrderLeft,
+ signedOrderRight,
+ takerAddress,
+ erc20BalancesByOwner,
+ erc721TokenIdsByOwner,
+ );
+ // Verify left order was partially filled
+ const leftOrderInfo: OrderInfo = await exchangeWrapper.getOrderInfoAsync(signedOrderLeft);
+ expect(leftOrderInfo.orderStatus as ExchangeStatus).to.be.equal(ExchangeStatus.ORDER_FULLY_FILLED);
+ // Verify right order was fully filled
+ const rightOrderInfo: OrderInfo = await exchangeWrapper.getOrderInfoAsync(signedOrderRight);
+ expect(rightOrderInfo.orderStatus as ExchangeStatus).to.be.equal(ExchangeStatus.ORDER_FILLABLE);
+ // 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 = orderFactoryLeft.newSignedOrder({
+ makerAddress: makerAddressLeft,
+ makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
+ takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
+ makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18),
+ takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(50), 18),
+ feeRecipientAddress: feeRecipientAddressLeft,
+ });
+ // Match signedOrderLeft2 with signedOrderRight
+ const leftTakerAssetFilledAmount = new BigNumber(0);
+ const takerAmountReceived = newERC20BalancesByOwner[takerAddress][defaultERC20MakerAssetAddress].minus(
+ erc20BalancesByOwner[takerAddress][defaultERC20MakerAssetAddress],
+ );
+ const rightTakerAssetFilledAmount = signedOrderLeft.makerAssetAmount.minus(takerAmountReceived);
+ await matchOrderTester.matchOrdersAndVerifyBalancesAsync(
+ signedOrderLeft2,
+ signedOrderRight,
+ takerAddress,
+ newERC20BalancesByOwner,
+ erc721TokenIdsByOwner,
+ leftTakerAssetFilledAmount,
+ rightTakerAssetFilledAmount,
+ );
+ // Verify second left order was partially filled
+ const leftOrderInfo2: OrderInfo = await exchangeWrapper.getOrderInfoAsync(signedOrderLeft2);
+ expect(leftOrderInfo2.orderStatus as ExchangeStatus).to.be.equal(ExchangeStatus.ORDER_FILLABLE);
+ // Verify right order was fully filled
+ const rightOrderInfo2: OrderInfo = await exchangeWrapper.getOrderInfoAsync(signedOrderRight);
+ expect(rightOrderInfo2.orderStatus as ExchangeStatus).to.be.equal(ExchangeStatus.ORDER_FULLY_FILLED);
+ });
+
+ it('should transfer the correct amounts if fee recipient is the same across both matched orders', async () => {
+ const feeRecipientAddress = feeRecipientAddressLeft;
+ const signedOrderLeft = orderFactoryLeft.newSignedOrder({
+ makerAddress: makerAddressLeft,
+ makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
+ takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
+ makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18),
+ takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
+ feeRecipientAddress,
+ });
+ const signedOrderRight = orderFactoryRight.newSignedOrder({
+ makerAddress: makerAddressRight,
+ makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
+ takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
+ makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
+ takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18),
+ feeRecipientAddress,
+ });
+ // Match orders
+ await matchOrderTester.matchOrdersAndVerifyBalancesAsync(
+ signedOrderLeft,
+ signedOrderRight,
+ takerAddress,
+ erc20BalancesByOwner,
+ erc721TokenIdsByOwner,
+ );
+ });
+
+ it('should transfer the correct amounts if taker is also the left order maker', async () => {
+ // Create orders to match
+ const signedOrderLeft = orderFactoryLeft.newSignedOrder({
+ makerAddress: makerAddressLeft,
+ makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
+ takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
+ makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18),
+ takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
+ feeRecipientAddress: feeRecipientAddressLeft,
+ });
+ const signedOrderRight = orderFactoryRight.newSignedOrder({
+ makerAddress: makerAddressRight,
+ makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
+ takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
+ makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
+ takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18),
+ feeRecipientAddress: feeRecipientAddressRight,
+ });
+ // Match orders
+ takerAddress = signedOrderLeft.makerAddress;
+ await matchOrderTester.matchOrdersAndVerifyBalancesAsync(
+ signedOrderLeft,
+ signedOrderRight,
+ takerAddress,
+ erc20BalancesByOwner,
+ erc721TokenIdsByOwner,
+ );
+ });
+
+ it('should transfer the correct amounts if taker is also the right order maker', async () => {
+ // Create orders to match
+ const signedOrderLeft = orderFactoryLeft.newSignedOrder({
+ makerAddress: makerAddressLeft,
+ makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
+ takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
+ makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18),
+ takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
+ feeRecipientAddress: feeRecipientAddressLeft,
+ });
+ const signedOrderRight = orderFactoryRight.newSignedOrder({
+ makerAddress: makerAddressRight,
+ makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
+ takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
+ makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
+ takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18),
+ feeRecipientAddress: feeRecipientAddressRight,
+ });
+ // Match orders
+ takerAddress = signedOrderRight.makerAddress;
+ await matchOrderTester.matchOrdersAndVerifyBalancesAsync(
+ signedOrderLeft,
+ signedOrderRight,
+ takerAddress,
+ erc20BalancesByOwner,
+ erc721TokenIdsByOwner,
+ );
+ });
+
+ it('should transfer the correct amounts if taker is also the left fee recipient', async () => {
+ // Create orders to match
+ const signedOrderLeft = orderFactoryLeft.newSignedOrder({
+ makerAddress: makerAddressLeft,
+ makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
+ takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
+ makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18),
+ takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
+ feeRecipientAddress: feeRecipientAddressLeft,
+ });
+ const signedOrderRight = orderFactoryRight.newSignedOrder({
+ makerAddress: makerAddressRight,
+ makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
+ takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
+ makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
+ takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18),
+ feeRecipientAddress: feeRecipientAddressRight,
+ });
+ // Match orders
+ takerAddress = feeRecipientAddressLeft;
+ await matchOrderTester.matchOrdersAndVerifyBalancesAsync(
+ signedOrderLeft,
+ signedOrderRight,
+ takerAddress,
+ erc20BalancesByOwner,
+ erc721TokenIdsByOwner,
+ );
+ });
+
+ it('should transfer the correct amounts if taker is also the right fee recipient', async () => {
+ // Create orders to match
+ const signedOrderLeft = orderFactoryLeft.newSignedOrder({
+ makerAddress: makerAddressLeft,
+ makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
+ takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
+ makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18),
+ takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
+ feeRecipientAddress: feeRecipientAddressLeft,
+ });
+ const signedOrderRight = orderFactoryRight.newSignedOrder({
+ makerAddress: makerAddressRight,
+ makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
+ takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
+ makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
+ takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18),
+ feeRecipientAddress: feeRecipientAddressRight,
+ });
+ // Match orders
+ takerAddress = feeRecipientAddressRight;
+ await matchOrderTester.matchOrdersAndVerifyBalancesAsync(
+ signedOrderLeft,
+ signedOrderRight,
+ takerAddress,
+ erc20BalancesByOwner,
+ erc721TokenIdsByOwner,
+ );
+ });
+
+ 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 = orderFactoryLeft.newSignedOrder({
+ makerAddress: makerAddressLeft,
+ makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
+ takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
+ makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18),
+ takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
+ feeRecipientAddress: makerAddressLeft,
+ });
+ const signedOrderRight = orderFactoryRight.newSignedOrder({
+ makerAddress: makerAddressRight,
+ makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
+ takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
+ makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
+ takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18),
+ feeRecipientAddress: makerAddressRight,
+ });
+ // Match orders
+ await matchOrderTester.matchOrdersAndVerifyBalancesAsync(
+ signedOrderLeft,
+ signedOrderRight,
+ takerAddress,
+ erc20BalancesByOwner,
+ erc721TokenIdsByOwner,
+ );
+ });
+
+ it('Should throw if left order is not fillable', async () => {
+ // Create orders to match
+ const signedOrderLeft = orderFactoryLeft.newSignedOrder({
+ makerAddress: makerAddressLeft,
+ makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
+ takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
+ makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18),
+ takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
+ feeRecipientAddress: feeRecipientAddressLeft,
+ });
+ const signedOrderRight = orderFactoryRight.newSignedOrder({
+ makerAddress: makerAddressRight,
+ makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
+ takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
+ makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
+ takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18),
+ feeRecipientAddress: feeRecipientAddressRight,
+ });
+ // Cancel left order
+ await exchangeWrapper.cancelOrderAsync(signedOrderLeft, signedOrderLeft.makerAddress);
+ // Match orders
+ return expect(
+ exchangeWrapper.matchOrdersAsync(signedOrderLeft, signedOrderRight, takerAddress),
+ ).to.be.rejectedWith(constants.REVERT);
+ });
+
+ it('Should throw if right order is not fillable', async () => {
+ // Create orders to match
+ const signedOrderLeft = orderFactoryLeft.newSignedOrder({
+ makerAddress: makerAddressLeft,
+ makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
+ takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
+ makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18),
+ takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
+ feeRecipientAddress: feeRecipientAddressLeft,
+ });
+ const signedOrderRight = orderFactoryRight.newSignedOrder({
+ makerAddress: makerAddressRight,
+ makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
+ takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
+ makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
+ takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18),
+ feeRecipientAddress: feeRecipientAddressRight,
+ });
+ // Cancel right order
+ await exchangeWrapper.cancelOrderAsync(signedOrderRight, signedOrderRight.makerAddress);
+ // Match orders
+ return expect(
+ exchangeWrapper.matchOrdersAsync(signedOrderLeft, signedOrderRight, takerAddress),
+ ).to.be.rejectedWith(constants.REVERT);
+ });
+
+ it('should throw if there is not a positive spread', async () => {
+ // Create orders to match
+ const signedOrderLeft = orderFactoryLeft.newSignedOrder({
+ makerAddress: makerAddressLeft,
+ makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
+ takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
+ makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18),
+ takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18),
+ feeRecipientAddress: feeRecipientAddressLeft,
+ });
+ const signedOrderRight = orderFactoryRight.newSignedOrder({
+ makerAddress: makerAddressRight,
+ makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
+ takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
+ makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(1), 18),
+ takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(200), 18),
+ feeRecipientAddress: feeRecipientAddressRight,
+ });
+ // Match orders
+ return expect(
+ matchOrderTester.matchOrdersAndVerifyBalancesAsync(
+ signedOrderLeft,
+ signedOrderRight,
+ takerAddress,
+ erc20BalancesByOwner,
+ erc721TokenIdsByOwner,
+ ),
+ ).to.be.rejectedWith(constants.REVERT);
+ });
+
+ it('should throw if the left maker asset is not equal to the right taker asset ', async () => {
+ // Create orders to match
+ const signedOrderLeft = orderFactoryLeft.newSignedOrder({
+ makerAddress: makerAddressLeft,
+ makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
+ takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
+ makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18),
+ takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
+ feeRecipientAddress: feeRecipientAddressLeft,
+ });
+ const signedOrderRight = orderFactoryRight.newSignedOrder({
+ makerAddress: makerAddressRight,
+ makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
+ takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
+ makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
+ takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18),
+ feeRecipientAddress: feeRecipientAddressRight,
+ });
+ // Match orders
+ return expect(
+ matchOrderTester.matchOrdersAndVerifyBalancesAsync(
+ signedOrderLeft,
+ signedOrderRight,
+ takerAddress,
+ erc20BalancesByOwner,
+ erc721TokenIdsByOwner,
+ ),
+ ).to.be.rejectedWith(constants.REVERT);
+ });
+
+ it('should throw if the right maker asset is not equal to the left taker asset', async () => {
+ // Create orders to match
+ const signedOrderLeft = orderFactoryLeft.newSignedOrder({
+ makerAddress: makerAddressLeft,
+ makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
+ takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
+ makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18),
+ takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
+ feeRecipientAddress: feeRecipientAddressLeft,
+ });
+ const signedOrderRight = orderFactoryRight.newSignedOrder({
+ makerAddress: makerAddressRight,
+ makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
+ takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
+ makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
+ takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18),
+ feeRecipientAddress: feeRecipientAddressRight,
+ });
+ // Match orders
+ return expect(
+ matchOrderTester.matchOrdersAndVerifyBalancesAsync(
+ signedOrderLeft,
+ signedOrderRight,
+ takerAddress,
+ erc20BalancesByOwner,
+ erc721TokenIdsByOwner,
+ ),
+ ).to.be.rejectedWith(constants.REVERT);
+ });
+
+ 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 = orderFactoryLeft.newSignedOrder({
+ makerAddress: makerAddressLeft,
+ makerAssetData: assetProxyUtils.encodeERC721ProxyData(defaultERC721AssetAddress, erc721TokenToTransfer),
+ takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
+ makerAssetAmount: new BigNumber(1),
+ takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
+ feeRecipientAddress: feeRecipientAddressLeft,
+ });
+ const signedOrderRight = orderFactoryRight.newSignedOrder({
+ makerAddress: makerAddressRight,
+ makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
+ takerAssetData: assetProxyUtils.encodeERC721ProxyData(defaultERC721AssetAddress, erc721TokenToTransfer),
+ makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
+ takerAssetAmount: new BigNumber(1),
+ feeRecipientAddress: feeRecipientAddressRight,
+ });
+ // Match orders
+ await matchOrderTester.matchOrdersAndVerifyBalancesAsync(
+ signedOrderLeft,
+ signedOrderRight,
+ takerAddress,
+ erc20BalancesByOwner,
+ erc721TokenIdsByOwner,
+ );
+ // Verify left order was fully filled
+ const leftOrderInfo: OrderInfo = await exchangeWrapper.getOrderInfoAsync(signedOrderLeft);
+ expect(leftOrderInfo.orderStatus as ExchangeStatus).to.be.equal(ExchangeStatus.ORDER_FULLY_FILLED);
+ // Verify right order was fully filled
+ const rightOrderInfo: OrderInfo = await exchangeWrapper.getOrderInfoAsync(signedOrderRight);
+ expect(rightOrderInfo.orderStatus as ExchangeStatus).to.be.equal(ExchangeStatus.ORDER_FULLY_FILLED);
+ });
+
+ 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 = orderFactoryLeft.newSignedOrder({
+ makerAddress: makerAddressLeft,
+ makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
+ takerAssetData: assetProxyUtils.encodeERC721ProxyData(defaultERC721AssetAddress, erc721TokenToTransfer),
+ makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
+ takerAssetAmount: new BigNumber(1),
+ feeRecipientAddress: feeRecipientAddressLeft,
+ });
+ const signedOrderRight = orderFactoryRight.newSignedOrder({
+ makerAddress: makerAddressRight,
+ makerAssetData: assetProxyUtils.encodeERC721ProxyData(defaultERC721AssetAddress, erc721TokenToTransfer),
+ takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
+ makerAssetAmount: new BigNumber(1),
+ takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
+ feeRecipientAddress: feeRecipientAddressRight,
+ });
+ // Match orders
+ await matchOrderTester.matchOrdersAndVerifyBalancesAsync(
+ signedOrderLeft,
+ signedOrderRight,
+ takerAddress,
+ erc20BalancesByOwner,
+ erc721TokenIdsByOwner,
+ );
+ // Verify left order was fully filled
+ const leftOrderInfo: OrderInfo = await exchangeWrapper.getOrderInfoAsync(signedOrderLeft);
+ expect(leftOrderInfo.orderStatus as ExchangeStatus).to.be.equal(ExchangeStatus.ORDER_FULLY_FILLED);
+ // Verify right order was fully filled
+ const rightOrderInfo: OrderInfo = await exchangeWrapper.getOrderInfoAsync(signedOrderRight);
+ expect(rightOrderInfo.orderStatus as ExchangeStatus).to.be.equal(ExchangeStatus.ORDER_FULLY_FILLED);
+ });
+ });
+}); // tslint:disable-line:max-file-line-count
diff --git a/packages/contracts/test/exchange/signature_validator.ts b/packages/contracts/test/exchange/signature_validator.ts
index 489ed32c5..91614c666 100644
--- a/packages/contracts/test/exchange/signature_validator.ts
+++ b/packages/contracts/test/exchange/signature_validator.ts
@@ -1,4 +1,3 @@
-import { ZeroEx } from '0x.js';
import { BlockchainLifecycle } from '@0xproject/dev-utils';
import { BigNumber } from '@0xproject/utils';
import * as chai from 'chai';
@@ -26,6 +25,12 @@ describe('MixinSignatureValidator', () => {
let signatureValidator: TestSignatureValidatorContract;
before(async () => {
+ await blockchainLifecycle.startAsync();
+ });
+ after(async () => {
+ await blockchainLifecycle.revertAsync();
+ });
+ before(async () => {
const accounts = await web3Wrapper.getAvailableAddressesAsync();
const makerAddress = accounts[0];
signatureValidator = await TestSignatureValidatorContract.deployFrom0xArtifactAsync(
@@ -33,7 +38,6 @@ describe('MixinSignatureValidator', () => {
provider,
txDefaults,
);
- const zeroEx = new ZeroEx(provider, { networkId: constants.TESTRPC_NETWORK_ID });
const defaultOrderParams = {
...constants.STATIC_ORDER_PARAMS,
@@ -62,12 +66,12 @@ describe('MixinSignatureValidator', () => {
it('should return true with a valid signature', async () => {
const orderHashHex = orderUtils.getOrderHashHex(signedOrder);
- const success = await signatureValidator.publicIsValidSignature.callAsync(
+ const isValidSignature = await signatureValidator.publicIsValidSignature.callAsync(
orderHashHex,
signedOrder.makerAddress,
signedOrder.signature,
);
- expect(success).to.be.true();
+ expect(isValidSignature).to.be.true();
});
it('should return false with an invalid signature', async () => {
@@ -81,12 +85,12 @@ describe('MixinSignatureValidator', () => {
const invalidSigHex = `0x${invalidSigBuff.toString('hex')}`;
signedOrder.signature = invalidSigHex;
const orderHashHex = orderUtils.getOrderHashHex(signedOrder);
- const success = await signatureValidator.publicIsValidSignature.callAsync(
+ const isValidSignature = await signatureValidator.publicIsValidSignature.callAsync(
orderHashHex,
signedOrder.makerAddress,
signedOrder.signature,
);
- expect(success).to.be.false();
+ expect(isValidSignature).to.be.false();
});
});
});
diff --git a/packages/contracts/test/exchange/transactions.ts b/packages/contracts/test/exchange/transactions.ts
index a71b50a61..24f7230fa 100644
--- a/packages/contracts/test/exchange/transactions.ts
+++ b/packages/contracts/test/exchange/transactions.ts
@@ -1,5 +1,3 @@
-import { ZeroEx } from '0x.js';
-
import { BlockchainLifecycle } from '@0xproject/dev-utils';
import { BigNumber } from '@0xproject/utils';
import * as chai from 'chai';
@@ -21,7 +19,7 @@ import { TransactionFactory } from '../../src/utils/transaction_factory';
import {
AssetProxyId,
ERC20BalancesByOwner,
- ExchangeContractErrs,
+ ExchangeStatus,
OrderStruct,
SignatureType,
SignedOrder,
@@ -59,8 +57,12 @@ describe('Exchange transactions', () => {
let defaultMakerTokenAddress: string;
let defaultTakerTokenAddress: string;
- let zeroEx: ZeroEx;
-
+ before(async () => {
+ await blockchainLifecycle.startAsync();
+ });
+ after(async () => {
+ await blockchainLifecycle.revertAsync();
+ });
before(async () => {
const accounts = await web3Wrapper.getAvailableAddressesAsync();
const usedAddresses = ([owner, senderAddress, makerAddress, takerAddress, feeRecipientAddress] = accounts);
@@ -77,14 +79,13 @@ describe('Exchange transactions', () => {
txDefaults,
assetProxyUtils.encodeERC20ProxyData(zrxToken.address),
);
- zeroEx = new ZeroEx(provider, {
- exchangeContractAddress: exchange.address,
- networkId: constants.TESTRPC_NETWORK_ID,
- });
- exchangeWrapper = new ExchangeWrapper(exchange, zeroEx);
+ exchangeWrapper = new ExchangeWrapper(exchange, provider);
await exchangeWrapper.registerAssetProxyAsync(AssetProxyId.ERC20, erc20Proxy.address, owner);
- await erc20Proxy.addAuthorizedAddress.sendTransactionAsync(exchange.address, { from: owner });
+ await web3Wrapper.awaitTransactionSuccessAsync(
+ await erc20Proxy.addAuthorizedAddress.sendTransactionAsync(exchange.address, { from: owner }),
+ constants.AWAIT_TRANSACTION_MINED_MS,
+ );
defaultMakerTokenAddress = erc20TokenA.address;
defaultTakerTokenAddress = erc20TokenB.address;
@@ -179,7 +180,7 @@ describe('Exchange transactions', () => {
it('should reset the currentContextAddress', async () => {
await exchangeWrapper.executeTransactionAsync(signedTx, senderAddress);
const currentContextAddress = await exchange.currentContextAddress.callAsync();
- expect(currentContextAddress).to.equal(ZeroEx.NULL_ADDRESS);
+ expect(currentContextAddress).to.equal(constants.NULL_ADDRESS);
});
});
@@ -197,7 +198,7 @@ describe('Exchange transactions', () => {
it('should cancel the order when signed by maker and called by sender', async () => {
await exchangeWrapper.executeTransactionAsync(signedTx, senderAddress);
- const res = await exchangeWrapper.fillOrderAsync(signedOrder, takerAddress);
+ const res = await exchangeWrapper.fillOrderAsync(signedOrder, senderAddress);
const newBalances = await erc20Wrapper.getBalancesAsync();
expect(newBalances).to.deep.equal(erc20Balances);
});
diff --git a/packages/contracts/test/exchange/wrapper.ts b/packages/contracts/test/exchange/wrapper.ts
index 44145b4f0..e7217c2a7 100644
--- a/packages/contracts/test/exchange/wrapper.ts
+++ b/packages/contracts/test/exchange/wrapper.ts
@@ -1,9 +1,9 @@
-import { ZeroEx } from '0x.js';
import { BlockchainLifecycle, devConstants, web3Factory } from '@0xproject/dev-utils';
import { BigNumber } from '@0xproject/utils';
import { Web3Wrapper } from '@0xproject/web3-wrapper';
import * as chai from 'chai';
import * as _ from 'lodash';
+import 'make-promises-safe';
import * as Web3 from 'web3';
import { DummyERC20TokenContract } from '../../src/contract_wrappers/generated/dummy_e_r_c20_token';
@@ -53,8 +53,12 @@ describe('Exchange wrappers', () => {
let defaultMakerAssetAddress: string;
let defaultTakerAssetAddress: string;
- let zeroEx: ZeroEx;
-
+ before(async () => {
+ await blockchainLifecycle.startAsync();
+ });
+ after(async () => {
+ await blockchainLifecycle.revertAsync();
+ });
before(async () => {
const accounts = await web3Wrapper.getAvailableAddressesAsync();
const usedAddresses = ([owner, makerAddress, takerAddress, feeRecipientAddress] = accounts);
@@ -79,20 +83,22 @@ describe('Exchange wrappers', () => {
txDefaults,
assetProxyUtils.encodeERC20ProxyData(zrxToken.address),
);
- zeroEx = new ZeroEx(provider, {
- exchangeContractAddress: exchange.address,
- networkId: constants.TESTRPC_NETWORK_ID,
- });
- exchangeWrapper = new ExchangeWrapper(exchange, zeroEx);
+ exchangeWrapper = new ExchangeWrapper(exchange, provider);
await exchangeWrapper.registerAssetProxyAsync(AssetProxyId.ERC20, erc20Proxy.address, owner);
await exchangeWrapper.registerAssetProxyAsync(AssetProxyId.ERC721, erc721Proxy.address, owner);
- await erc20Proxy.addAuthorizedAddress.sendTransactionAsync(exchange.address, {
- from: owner,
- });
- await erc721Proxy.addAuthorizedAddress.sendTransactionAsync(exchange.address, {
- from: 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,
+ );
defaultMakerAssetAddress = erc20TokenA.address;
defaultTakerAssetAddress = erc20TokenB.address;
@@ -118,8 +124,8 @@ describe('Exchange wrappers', () => {
describe('fillOrKillOrder', () => {
it('should transfer the correct amounts', async () => {
const signedOrder = orderFactory.newSignedOrder({
- makerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(100), 18),
- takerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(200), 18),
+ 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, {
@@ -186,8 +192,8 @@ describe('Exchange wrappers', () => {
describe('fillOrderNoThrow', () => {
it('should transfer the correct amounts', async () => {
const signedOrder = orderFactory.newSignedOrder({
- makerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(100), 18),
- takerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(200), 18),
+ 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, {
@@ -230,7 +236,7 @@ describe('Exchange wrappers', () => {
it('should not change erc20Balances if maker erc20Balances are too low to fill order', async () => {
const signedOrder = orderFactory.newSignedOrder({
- makerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(100000), 18),
+ makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100000), 18),
});
await exchangeWrapper.fillOrderNoThrowAsync(signedOrder, takerAddress);
@@ -240,7 +246,7 @@ describe('Exchange wrappers', () => {
it('should not change erc20Balances if taker erc20Balances are too low to fill order', async () => {
const signedOrder = orderFactory.newSignedOrder({
- takerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(100000), 18),
+ takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100000), 18),
});
await exchangeWrapper.fillOrderNoThrowAsync(signedOrder, takerAddress);
@@ -250,13 +256,19 @@ describe('Exchange wrappers', () => {
it('should not change erc20Balances if maker allowances are too low to fill order', async () => {
const signedOrder = orderFactory.newSignedOrder();
- await erc20TokenA.approve.sendTransactionAsync(erc20Proxy.address, new BigNumber(0), {
- from: makerAddress,
- });
+ 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 erc20TokenA.approve.sendTransactionAsync(erc20Proxy.address, constants.INITIAL_ERC20_ALLOWANCE, {
- from: makerAddress,
- });
+ 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);
@@ -264,13 +276,19 @@ describe('Exchange wrappers', () => {
it('should not change erc20Balances if taker allowances are too low to fill order', async () => {
const signedOrder = orderFactory.newSignedOrder();
- await erc20TokenB.approve.sendTransactionAsync(erc20Proxy.address, new BigNumber(0), {
- from: takerAddress,
- });
+ 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 erc20TokenB.approve.sendTransactionAsync(erc20Proxy.address, constants.INITIAL_ERC20_ALLOWANCE, {
- from: 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);
@@ -619,7 +637,7 @@ describe('Exchange wrappers', () => {
});
it('should fill all signedOrders if cannot fill entire takerAssetFillAmount', async () => {
- const takerAssetFillAmount = ZeroEx.toBaseUnitAmount(new BigNumber(100000), 18);
+ const takerAssetFillAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(100000), 18);
_.forEach(signedOrders, signedOrder => {
erc20Balances[makerAddress][defaultMakerAssetAddress] = erc20Balances[makerAddress][
defaultMakerAssetAddress
@@ -662,7 +680,7 @@ describe('Exchange wrappers', () => {
return expect(
exchangeWrapper.marketSellOrdersAsync(signedOrders, takerAddress, {
- takerAssetFillAmount: ZeroEx.toBaseUnitAmount(new BigNumber(1000), 18),
+ takerAssetFillAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(1000), 18),
}),
).to.be.rejectedWith(constants.REVERT);
});
@@ -708,7 +726,7 @@ describe('Exchange wrappers', () => {
});
it('should fill all signedOrders if cannot fill entire takerAssetFillAmount', async () => {
- const takerAssetFillAmount = ZeroEx.toBaseUnitAmount(new BigNumber(100000), 18);
+ const takerAssetFillAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(100000), 18);
_.forEach(signedOrders, signedOrder => {
erc20Balances[makerAddress][defaultMakerAssetAddress] = erc20Balances[makerAddress][
defaultMakerAssetAddress
@@ -751,7 +769,7 @@ describe('Exchange wrappers', () => {
return expect(
exchangeWrapper.marketSellOrdersNoThrowAsync(signedOrders, takerAddress, {
- takerAssetFillAmount: ZeroEx.toBaseUnitAmount(new BigNumber(1000), 18),
+ takerAssetFillAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(1000), 18),
}),
).to.be.rejectedWith(constants.REVERT);
});
@@ -797,7 +815,7 @@ describe('Exchange wrappers', () => {
});
it('should fill all signedOrders if cannot fill entire makerAssetFillAmount', async () => {
- const makerAssetFillAmount = ZeroEx.toBaseUnitAmount(new BigNumber(100000), 18);
+ const makerAssetFillAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(100000), 18);
_.forEach(signedOrders, signedOrder => {
erc20Balances[makerAddress][defaultMakerAssetAddress] = erc20Balances[makerAddress][
defaultMakerAssetAddress
@@ -840,7 +858,7 @@ describe('Exchange wrappers', () => {
return expect(
exchangeWrapper.marketBuyOrdersAsync(signedOrders, takerAddress, {
- makerAssetFillAmount: ZeroEx.toBaseUnitAmount(new BigNumber(1000), 18),
+ makerAssetFillAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(1000), 18),
}),
).to.be.rejectedWith(constants.REVERT);
});
@@ -886,7 +904,7 @@ describe('Exchange wrappers', () => {
});
it('should fill all signedOrders if cannot fill entire takerAssetFillAmount', async () => {
- const takerAssetFillAmount = ZeroEx.toBaseUnitAmount(new BigNumber(100000), 18);
+ const takerAssetFillAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(100000), 18);
_.forEach(signedOrders, signedOrder => {
erc20Balances[makerAddress][defaultMakerAssetAddress] = erc20Balances[makerAddress][
defaultMakerAssetAddress
@@ -929,7 +947,7 @@ describe('Exchange wrappers', () => {
return expect(
exchangeWrapper.marketBuyOrdersNoThrowAsync(signedOrders, takerAddress, {
- makerAssetFillAmount: ZeroEx.toBaseUnitAmount(new BigNumber(1000), 18),
+ makerAssetFillAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(1000), 18),
}),
).to.be.rejectedWith(constants.REVERT);
});
diff --git a/packages/contracts/test/global_hooks.ts b/packages/contracts/test/global_hooks.ts
index 089521d94..8b48b030d 100644
--- a/packages/contracts/test/global_hooks.ts
+++ b/packages/contracts/test/global_hooks.ts
@@ -1,4 +1,6 @@
-import { coverage, env, EnvVars } from '@0xproject/dev-utils';
+import { env, EnvVars } from '@0xproject/dev-utils';
+
+import { coverage } from '../src/utils/coverage';
after('generate coverage report', async () => {
if (env.parseBoolean(EnvVars.SolidityCoverage)) {
diff --git a/packages/contracts/test/libraries/lib_bytes.ts b/packages/contracts/test/libraries/lib_bytes.ts
index 5ed5c356f..ce3adbdae 100644
--- a/packages/contracts/test/libraries/lib_bytes.ts
+++ b/packages/contracts/test/libraries/lib_bytes.ts
@@ -1,5 +1,5 @@
-import { LogWithDecodedArgs, TransactionReceiptWithDecodedLogs, ZeroEx } from '0x.js';
import { BlockchainLifecycle, devConstants, web3Factory } from '@0xproject/dev-utils';
+import { LogWithDecodedArgs, TransactionReceiptWithDecodedLogs } from '@0xproject/types';
import { BigNumber } from '@0xproject/utils';
import { Web3Wrapper } from '@0xproject/web3-wrapper';
import BN = require('bn.js');
@@ -33,6 +33,12 @@ describe('LibBytes', () => {
const testUint256 = new BigNumber(testBytes32, 16);
before(async () => {
+ await blockchainLifecycle.startAsync();
+ });
+ after(async () => {
+ await blockchainLifecycle.revertAsync();
+ });
+ before(async () => {
// Setup accounts & addresses
const accounts = await web3Wrapper.getAvailableAddressesAsync();
owner = accounts[0];
diff --git a/packages/contracts/test/multi_sig_with_time_lock.ts b/packages/contracts/test/multi_sig_with_time_lock.ts
index d39fa6d09..732077df2 100644
--- a/packages/contracts/test/multi_sig_with_time_lock.ts
+++ b/packages/contracts/test/multi_sig_with_time_lock.ts
@@ -1,8 +1,9 @@
-import { LogWithDecodedArgs, ZeroEx } from '0x.js';
import { BlockchainLifecycle, web3Factory } from '@0xproject/dev-utils';
+import { LogWithDecodedArgs } from '@0xproject/types';
import { AbiDecoder, BigNumber } from '@0xproject/utils';
import { Web3Wrapper } from '@0xproject/web3-wrapper';
import * as chai from 'chai';
+import 'make-promises-safe';
import * as Web3 from 'web3';
import * as multiSigWalletJSON from '../../build/contracts/MultiSigWalletWithTimeLock.json';
@@ -19,7 +20,6 @@ const MULTI_SIG_ABI = artifacts.MultiSigWalletWithTimeLock.compilerOutput.abi;
chaiSetup.configure();
const expect = chai.expect;
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
-const zeroEx = new ZeroEx(provider, { networkId: constants.TESTRPC_NETWORK_ID });
const abiDecoder = new AbiDecoder([MULTI_SIG_ABI]);
describe('MultiSigWalletWithTimeLock', () => {
@@ -44,6 +44,12 @@ describe('MultiSigWalletWithTimeLock', () => {
describe('changeTimeLock', () => {
describe('initially non-time-locked', async () => {
+ before(async () => {
+ await blockchainLifecycle.startAsync();
+ });
+ after(async () => {
+ await blockchainLifecycle.revertAsync();
+ });
before('deploy a wallet', async () => {
multiSig = await MultiSigWalletWithTimeLockContract.deployFrom0xArtifactAsync(
artifacts.MultiSigWalletWithTimeLock,
@@ -73,8 +79,11 @@ describe('MultiSigWalletWithTimeLock', () => {
args: [SECONDS_TIME_LOCKED.toNumber()],
};
const txHash = await multiSigWrapper.submitTransactionAsync(destination, from, dataParams);
- const subRes = await zeroEx.awaitTransactionMinedAsync(txHash);
- const log = abiDecoder.tryToDecodeLogOrNoop(subRes.logs[0]) as LogWithDecodedArgs<
+ const txReceipt = await web3Wrapper.awaitTransactionSuccessAsync(
+ txHash,
+ constants.AWAIT_TRANSACTION_MINED_MS,
+ );
+ const log = abiDecoder.tryToDecodeLogOrNoop(txReceipt.logs[0]) as LogWithDecodedArgs<
SubmissionContractEventArgs
>;
@@ -93,14 +102,20 @@ describe('MultiSigWalletWithTimeLock', () => {
args: [SECONDS_TIME_LOCKED.toNumber()],
};
let txHash = await multiSigWrapper.submitTransactionAsync(destination, from, dataParams);
- const subRes = await zeroEx.awaitTransactionMinedAsync(txHash);
- const log = abiDecoder.tryToDecodeLogOrNoop(subRes.logs[0]) as LogWithDecodedArgs<
+ const txReceipt = await web3Wrapper.awaitTransactionSuccessAsync(
+ txHash,
+ constants.AWAIT_TRANSACTION_MINED_MS,
+ );
+ const log = abiDecoder.tryToDecodeLogOrNoop(txReceipt.logs[0]) as LogWithDecodedArgs<
SubmissionContractEventArgs
>;
txId = log.args.transactionId;
txHash = await multiSig.confirmTransaction.sendTransactionAsync(txId, { from: owners[1] });
- const res = await zeroEx.awaitTransactionMinedAsync(txHash);
+ const res = await web3Wrapper.awaitTransactionSuccessAsync(
+ txHash,
+ constants.AWAIT_TRANSACTION_MINED_MS,
+ );
expect(res.logs).to.have.length(2);
const blockNum = await web3Wrapper.getBlockNumberAsync();
@@ -120,18 +135,25 @@ describe('MultiSigWalletWithTimeLock', () => {
args: [SECONDS_TIME_LOCKED.toNumber()],
};
let txHash = await multiSigWrapper.submitTransactionAsync(destination, from, dataParams);
- const subRes = await zeroEx.awaitTransactionMinedAsync(txHash);
- const log = abiDecoder.tryToDecodeLogOrNoop(subRes.logs[0]) as LogWithDecodedArgs<
+ const txReceipt = await web3Wrapper.awaitTransactionSuccessAsync(
+ txHash,
+ constants.AWAIT_TRANSACTION_MINED_MS,
+ );
+ const log = abiDecoder.tryToDecodeLogOrNoop(txReceipt.logs[0]) as LogWithDecodedArgs<
SubmissionContractEventArgs
>;
txId = log.args.transactionId;
txHash = await multiSig.confirmTransaction.sendTransactionAsync(txId, { from: owners[1] });
+ await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
expect(initialSecondsTimeLocked).to.be.equal(0);
txHash = await multiSig.executeTransaction.sendTransactionAsync(txId, { from: owners[0] });
- const res = await zeroEx.awaitTransactionMinedAsync(txHash);
+ const res = await web3Wrapper.awaitTransactionSuccessAsync(
+ txHash,
+ constants.AWAIT_TRANSACTION_MINED_MS,
+ );
expect(res.logs).to.have.length(2);
const secondsTimeLocked = new BigNumber(await multiSig.secondsTimeLocked.callAsync());
@@ -139,6 +161,12 @@ describe('MultiSigWalletWithTimeLock', () => {
});
});
describe('initially time-locked', async () => {
+ before(async () => {
+ await blockchainLifecycle.startAsync();
+ });
+ after(async () => {
+ await blockchainLifecycle.revertAsync();
+ });
before('deploy a wallet', async () => {
multiSig = await MultiSigWalletWithTimeLockContract.deployFrom0xArtifactAsync(
artifacts.MultiSigWalletWithTimeLock,
@@ -160,16 +188,22 @@ describe('MultiSigWalletWithTimeLock', () => {
args: [newSecondsTimeLocked],
};
let txHash = await multiSigWrapper.submitTransactionAsync(destination, from, dataParams);
- const subRes = await zeroEx.awaitTransactionMinedAsync(txHash);
- const log = abiDecoder.tryToDecodeLogOrNoop(subRes.logs[0]) as LogWithDecodedArgs<
+ let txReceipt = await web3Wrapper.awaitTransactionSuccessAsync(
+ txHash,
+ constants.AWAIT_TRANSACTION_MINED_MS,
+ );
+ const log = abiDecoder.tryToDecodeLogOrNoop(txReceipt.logs[0]) as LogWithDecodedArgs<
SubmissionContractEventArgs
>;
txId = log.args.transactionId;
txHash = await multiSig.confirmTransaction.sendTransactionAsync(txId, {
from: owners[1],
});
- const confRes = await zeroEx.awaitTransactionMinedAsync(txHash);
- expect(confRes.logs).to.have.length(2);
+ txReceipt = await web3Wrapper.awaitTransactionSuccessAsync(
+ txHash,
+ constants.AWAIT_TRANSACTION_MINED_MS,
+ );
+ expect(txReceipt.logs).to.have.length(2);
});
const newSecondsTimeLocked = 0;
it('should throw if it has enough confirmations but is not past the time lock', async () => {
@@ -180,7 +214,10 @@ describe('MultiSigWalletWithTimeLock', () => {
it('should execute if it has enough confirmations and is past the time lock', async () => {
await web3Wrapper.increaseTimeAsync(SECONDS_TIME_LOCKED.toNumber());
- await multiSig.executeTransaction.sendTransactionAsync(txId, { from: owners[0] });
+ await web3Wrapper.awaitTransactionSuccessAsync(
+ await multiSig.executeTransaction.sendTransactionAsync(txId, { from: owners[0] }),
+ constants.AWAIT_TRANSACTION_MINED_MS,
+ );
const secondsTimeLocked = new BigNumber(await multiSig.secondsTimeLocked.callAsync());
expect(secondsTimeLocked).to.be.bignumber.equal(newSecondsTimeLocked);
diff --git a/packages/contracts/test/multi_sig_with_time_lock_except_remove_auth_addr.ts b/packages/contracts/test/multi_sig_with_time_lock_except_remove_auth_addr.ts
index 65cd5c5a8..dcd206b1c 100644
--- a/packages/contracts/test/multi_sig_with_time_lock_except_remove_auth_addr.ts
+++ b/packages/contracts/test/multi_sig_with_time_lock_except_remove_auth_addr.ts
@@ -9,6 +9,7 @@ import { BlockchainLifecycle, devConstants, web3Factory } from '@0xproject/dev-u
import { AbiDecoder, BigNumber } from '@0xproject/utils';
import { Web3Wrapper } from '@0xproject/web3-wrapper';
import * as chai from 'chai';
+import 'make-promises-safe';
import * as Web3 from 'web3';
import { MultiSigWalletContract } from '../src/contract_wrappers/generated/multi_sig_wallet';
diff --git a/packages/contracts/test/token_registry.ts b/packages/contracts/test/token_registry.ts
index 1cae3da52..1cc519c53 100644
--- a/packages/contracts/test/token_registry.ts
+++ b/packages/contracts/test/token_registry.ts
@@ -1,10 +1,10 @@
-import { ZeroEx } from '0x.js';
import { BlockchainLifecycle, devConstants, web3Factory } from '@0xproject/dev-utils';
import { BigNumber, NULL_BYTES } from '@0xproject/utils';
import { Web3Wrapper } from '@0xproject/web3-wrapper';
import * as chai from 'chai';
import ethUtil = require('ethereumjs-util');
import * as _ from 'lodash';
+import 'make-promises-safe';
import * as Web3 from 'web3';
import { TokenRegistryContract } from '../src/contract_wrappers/generated/token_registry';
@@ -24,6 +24,12 @@ describe('TokenRegistry', () => {
let tokenReg: TokenRegistryContract;
let tokenRegWrapper: TokenRegWrapper;
before(async () => {
+ await blockchainLifecycle.startAsync();
+ });
+ after(async () => {
+ await blockchainLifecycle.revertAsync();
+ });
+ before(async () => {
const accounts = await web3Wrapper.getAvailableAddressesAsync();
owner = accounts[0];
notOwner = accounts[1];
@@ -59,7 +65,7 @@ describe('TokenRegistry', () => {
};
const nullToken = {
- address: ZeroEx.NULL_ADDRESS,
+ address: constants.NULL_ADDRESS,
name: '',
symbol: '',
decimals: 0,
@@ -136,9 +142,12 @@ describe('TokenRegistry', () => {
});
it('should change the token name when called by owner', async () => {
- await tokenReg.setTokenName.sendTransactionAsync(token1.address, token2.name, {
- from: owner,
- });
+ await web3Wrapper.awaitTransactionSuccessAsync(
+ await tokenReg.setTokenName.sendTransactionAsync(token1.address, token2.name, {
+ from: owner,
+ }),
+ constants.AWAIT_TRANSACTION_MINED_MS,
+ );
const [newData, oldData] = await Promise.all([
tokenRegWrapper.getTokenByNameAsync(token2.name),
tokenRegWrapper.getTokenByNameAsync(token1.name),
@@ -175,7 +184,10 @@ describe('TokenRegistry', () => {
});
it('should change the token symbol when called by owner', async () => {
- await tokenReg.setTokenSymbol.sendTransactionAsync(token1.address, token2.symbol, { from: owner });
+ await web3Wrapper.awaitTransactionSuccessAsync(
+ await tokenReg.setTokenSymbol.sendTransactionAsync(token1.address, token2.symbol, { from: owner }),
+ constants.AWAIT_TRANSACTION_MINED_MS,
+ );
const [newData, oldData] = await Promise.all([
tokenRegWrapper.getTokenBySymbolAsync(token2.symbol),
tokenRegWrapper.getTokenBySymbolAsync(token1.symbol),
@@ -216,9 +228,12 @@ describe('TokenRegistry', () => {
it('should remove token metadata when called by owner', async () => {
const index = new BigNumber(0);
- await tokenReg.removeToken.sendTransactionAsync(token1.address, index, {
- from: owner,
- });
+ await web3Wrapper.awaitTransactionSuccessAsync(
+ await tokenReg.removeToken.sendTransactionAsync(token1.address, index, {
+ from: owner,
+ }),
+ constants.AWAIT_TRANSACTION_MINED_MS,
+ );
const tokenData = await tokenRegWrapper.getTokenMetaDataAsync(token1.address);
expect(tokenData).to.be.deep.equal(nullToken);
});
diff --git a/packages/contracts/test/tutorials/arbitrage.ts b/packages/contracts/test/tutorials/arbitrage.ts
index c65e050ad..e5787d5b8 100644
--- a/packages/contracts/test/tutorials/arbitrage.ts
+++ b/packages/contracts/test/tutorials/arbitrage.ts
@@ -4,6 +4,7 @@
// import { BigNumber } from '@0xproject/utils';
// import { Web3Wrapper } from '@0xproject/web3-wrapper';
// import * as chai from 'chai';
+// import 'make-promises-safe';
// import ethUtil = require('ethereumjs-util');
// import * as Web3 from 'web3';
@@ -37,7 +38,7 @@
// let amountGive: BigNumber;
// let makerTokenAmount: BigNumber;
// let takerTokenAmount: BigNumber;
-// const feeRecipient = ZeroEx.NULL_ADDRESS;
+// const feeRecipient = constants.NULL_ADDRESS;
// const INITIAL_BALANCE = ZeroEx.toBaseUnitAmount(new BigNumber(10000), 18);
// const INITIAL_ALLOWANCE = ZeroEx.toBaseUnitAmount(new BigNumber(10000), 18);
@@ -114,7 +115,7 @@
// exchangeContractAddress: exchange.address,
// networkId: constants.TESTRPC_NETWORK_ID,
// });
-// exWrapper = new ExchangeWrapper(exchange, zeroEx);
+// exWrapper = new ExchangeWrapper(exchange, provider);
// makerTokenAmount = ZeroEx.toBaseUnitAmount(new BigNumber(1), 18);
// takerTokenAmount = makerTokenAmount;
diff --git a/packages/contracts/test/unlimited_allowance_token.ts b/packages/contracts/test/unlimited_allowance_token.ts
index 8721930be..e3e3bd485 100644
--- a/packages/contracts/test/unlimited_allowance_token.ts
+++ b/packages/contracts/test/unlimited_allowance_token.ts
@@ -1,8 +1,9 @@
-import { ZeroEx } from '0x.js';
+import { ContractWrappers } from '@0xproject/contract-wrappers';
import { BlockchainLifecycle, devConstants, web3Factory } from '@0xproject/dev-utils';
import { BigNumber } from '@0xproject/utils';
import { Web3Wrapper } from '@0xproject/web3-wrapper';
import * as chai from 'chai';
+import 'make-promises-safe';
import * as Web3 from 'web3';
import { DummyERC20TokenContract } from '../src/contract_wrappers/generated/dummy_e_r_c20_token';
@@ -18,7 +19,7 @@ const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
describe('UnlimitedAllowanceToken', () => {
let owner: string;
let spender: string;
- const zeroEx = new ZeroEx(provider, {
+ const contractWrappers = new ContractWrappers(provider, {
networkId: constants.TESTRPC_NETWORK_ID,
});
@@ -27,6 +28,12 @@ describe('UnlimitedAllowanceToken', () => {
let token: DummyERC20TokenContract;
before(async () => {
+ await blockchainLifecycle.startAsync();
+ });
+ after(async () => {
+ await blockchainLifecycle.revertAsync();
+ });
+ before(async () => {
const accounts = await web3Wrapper.getAvailableAddressesAsync();
owner = accounts[0];
spender = accounts[1];
@@ -39,7 +46,10 @@ describe('UnlimitedAllowanceToken', () => {
constants.DUMMY_TOKEN_DECIMALS,
constants.DUMMY_TOKEN_TOTAL_SUPPLY,
);
- await token.mint.sendTransactionAsync(MAX_MINT_VALUE, { from: owner });
+ await web3Wrapper.awaitTransactionSuccessAsync(
+ await token.mint.sendTransactionAsync(MAX_MINT_VALUE, { from: owner }),
+ constants.AWAIT_TRANSACTION_MINED_MS,
+ );
tokenAddress = token.address;
});
beforeEach(async () => {
@@ -50,7 +60,7 @@ describe('UnlimitedAllowanceToken', () => {
});
describe('transfer', () => {
it('should throw if owner has insufficient balance', async () => {
- const ownerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner);
+ const ownerBalance = await contractWrappers.token.getBalanceAsync(tokenAddress, owner);
const amountToTransfer = ownerBalance.plus(1);
return expect(token.transfer.callAsync(spender, amountToTransfer, { from: owner })).to.be.rejectedWith(
constants.REVERT,
@@ -59,11 +69,11 @@ describe('UnlimitedAllowanceToken', () => {
it('should transfer balance from sender to receiver', async () => {
const receiver = spender;
- const initOwnerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner);
+ const initOwnerBalance = await contractWrappers.token.getBalanceAsync(tokenAddress, owner);
const amountToTransfer = new BigNumber(1);
- await zeroEx.token.transferAsync(tokenAddress, owner, receiver, amountToTransfer);
- const finalOwnerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner);
- const finalReceiverBalance = await zeroEx.token.getBalanceAsync(tokenAddress, receiver);
+ await contractWrappers.token.transferAsync(tokenAddress, owner, receiver, amountToTransfer);
+ const finalOwnerBalance = await contractWrappers.token.getBalanceAsync(tokenAddress, owner);
+ const finalReceiverBalance = await contractWrappers.token.getBalanceAsync(tokenAddress, receiver);
const expectedFinalOwnerBalance = initOwnerBalance.minus(amountToTransfer);
const expectedFinalReceiverBalance = amountToTransfer;
@@ -81,9 +91,9 @@ describe('UnlimitedAllowanceToken', () => {
describe('transferFrom', () => {
it('should throw if owner has insufficient balance', async () => {
- const ownerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner);
+ const ownerBalance = await contractWrappers.token.getBalanceAsync(tokenAddress, owner);
const amountToTransfer = ownerBalance.plus(1);
- await zeroEx.token.setAllowanceAsync(tokenAddress, owner, spender, amountToTransfer);
+ await contractWrappers.token.setAllowanceAsync(tokenAddress, owner, spender, amountToTransfer);
return expect(
token.transferFrom.callAsync(owner, spender, amountToTransfer, {
from: spender,
@@ -92,12 +102,12 @@ describe('UnlimitedAllowanceToken', () => {
});
it('should throw if spender has insufficient allowance', async () => {
- const ownerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner);
+ const ownerBalance = await contractWrappers.token.getBalanceAsync(tokenAddress, owner);
const amountToTransfer = ownerBalance;
- const spenderAllowance = await zeroEx.token.getAllowanceAsync(tokenAddress, owner, spender);
- const spenderAllowanceIsInsufficient = spenderAllowance.cmp(amountToTransfer) < 0;
- expect(spenderAllowanceIsInsufficient).to.be.true();
+ const spenderAllowance = await contractWrappers.token.getAllowanceAsync(tokenAddress, owner, spender);
+ const isSpenderAllowanceInsufficient = spenderAllowance.cmp(amountToTransfer) < 0;
+ expect(isSpenderAllowanceInsufficient).to.be.true();
return expect(
token.transferFrom.callAsync(owner, spender, amountToTransfer, {
@@ -115,44 +125,44 @@ describe('UnlimitedAllowanceToken', () => {
});
it('should not modify spender allowance if spender allowance is 2^256 - 1', async () => {
- const initOwnerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner);
+ const initOwnerBalance = await contractWrappers.token.getBalanceAsync(tokenAddress, owner);
const amountToTransfer = initOwnerBalance;
- const initSpenderAllowance = zeroEx.token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS;
- await zeroEx.token.setAllowanceAsync(tokenAddress, owner, spender, initSpenderAllowance);
- await zeroEx.token.transferFromAsync(tokenAddress, owner, spender, spender, amountToTransfer, {
+ const initSpenderAllowance = contractWrappers.token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS;
+ await contractWrappers.token.setAllowanceAsync(tokenAddress, owner, spender, initSpenderAllowance);
+ await contractWrappers.token.transferFromAsync(tokenAddress, owner, spender, spender, amountToTransfer, {
gasLimit: constants.MAX_TOKEN_TRANSFERFROM_GAS,
});
- const newSpenderAllowance = await zeroEx.token.getAllowanceAsync(tokenAddress, owner, spender);
+ const newSpenderAllowance = await contractWrappers.token.getAllowanceAsync(tokenAddress, owner, spender);
expect(initSpenderAllowance).to.be.bignumber.equal(newSpenderAllowance);
});
it('should transfer the correct balances if spender has sufficient allowance', async () => {
- const initOwnerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner);
+ const initOwnerBalance = await contractWrappers.token.getBalanceAsync(tokenAddress, owner);
const amountToTransfer = initOwnerBalance;
const initSpenderAllowance = initOwnerBalance;
- await zeroEx.token.setAllowanceAsync(tokenAddress, owner, spender, initSpenderAllowance);
- await zeroEx.token.transferFromAsync(tokenAddress, owner, spender, spender, amountToTransfer, {
+ await contractWrappers.token.setAllowanceAsync(tokenAddress, owner, spender, initSpenderAllowance);
+ await contractWrappers.token.transferFromAsync(tokenAddress, owner, spender, spender, amountToTransfer, {
gasLimit: constants.MAX_TOKEN_TRANSFERFROM_GAS,
});
- const newOwnerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner);
- const newSpenderBalance = await zeroEx.token.getBalanceAsync(tokenAddress, spender);
+ const newOwnerBalance = await contractWrappers.token.getBalanceAsync(tokenAddress, owner);
+ const newSpenderBalance = await contractWrappers.token.getBalanceAsync(tokenAddress, spender);
expect(newOwnerBalance).to.be.bignumber.equal(0);
expect(newSpenderBalance).to.be.bignumber.equal(initOwnerBalance);
});
it('should modify allowance if spender has sufficient allowance less than 2^256 - 1', async () => {
- const initOwnerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner);
+ const initOwnerBalance = await contractWrappers.token.getBalanceAsync(tokenAddress, owner);
const amountToTransfer = initOwnerBalance;
const initSpenderAllowance = initOwnerBalance;
- await zeroEx.token.setAllowanceAsync(tokenAddress, owner, spender, initSpenderAllowance);
- await zeroEx.token.transferFromAsync(tokenAddress, owner, spender, spender, amountToTransfer, {
+ await contractWrappers.token.setAllowanceAsync(tokenAddress, owner, spender, initSpenderAllowance);
+ await contractWrappers.token.transferFromAsync(tokenAddress, owner, spender, spender, amountToTransfer, {
gasLimit: constants.MAX_TOKEN_TRANSFERFROM_GAS,
});
- const newSpenderAllowance = await zeroEx.token.getAllowanceAsync(tokenAddress, owner, spender);
+ const newSpenderAllowance = await contractWrappers.token.getAllowanceAsync(tokenAddress, owner, spender);
expect(newSpenderAllowance).to.be.bignumber.equal(0);
});
});
diff --git a/packages/contracts/test/zrx_token.ts b/packages/contracts/test/zrx_token.ts
index 5de751780..e632364ef 100644
--- a/packages/contracts/test/zrx_token.ts
+++ b/packages/contracts/test/zrx_token.ts
@@ -1,8 +1,9 @@
-import { ZeroEx } from '0x.js';
+import { ContractWrappers } from '@0xproject/contract-wrappers';
import { BlockchainLifecycle, devConstants, web3Factory } from '@0xproject/dev-utils';
import { BigNumber } from '@0xproject/utils';
import { Web3Wrapper } from '@0xproject/web3-wrapper';
import * as chai from 'chai';
+import 'make-promises-safe';
import * as Web3 from 'web3';
import { ZRXTokenContract } from '../src/contract_wrappers/generated/zrx_token';
@@ -18,7 +19,7 @@ const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
describe('ZRXToken', () => {
let owner: string;
let spender: string;
- let zeroEx: ZeroEx;
+ let contractWrappers: ContractWrappers;
let MAX_UINT: BigNumber;
@@ -26,15 +27,21 @@ describe('ZRXToken', () => {
let zrxAddress: string;
before(async () => {
+ await blockchainLifecycle.startAsync();
+ });
+ after(async () => {
+ await blockchainLifecycle.revertAsync();
+ });
+ before(async () => {
const accounts = await web3Wrapper.getAvailableAddressesAsync();
owner = accounts[0];
spender = accounts[1];
- zeroEx = new ZeroEx(provider, {
+ contractWrappers = new ContractWrappers(provider, {
networkId: constants.TESTRPC_NETWORK_ID,
});
zrxToken = await ZRXTokenContract.deployFrom0xArtifactAsync(artifacts.ZRX, provider, txDefaults);
zrxAddress = zrxToken.address;
- MAX_UINT = zeroEx.token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS;
+ MAX_UINT = contractWrappers.token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS;
});
beforeEach(async () => {
await blockchainLifecycle.startAsync();
@@ -52,7 +59,7 @@ describe('ZRXToken', () => {
it('should have a total supply of 1 billion tokens', async () => {
const totalSupply = new BigNumber(await zrxToken.totalSupply.callAsync());
const expectedTotalSupply = 1000000000;
- expect(ZeroEx.toUnitAmount(totalSupply, 18)).to.be.bignumber.equal(expectedTotalSupply);
+ expect(Web3Wrapper.toUnitAmount(totalSupply, 18)).to.be.bignumber.equal(expectedTotalSupply);
});
it('should be named 0x Protocol Token', async () => {
@@ -70,7 +77,7 @@ describe('ZRXToken', () => {
describe('constructor', () => {
it('should initialize owner balance to totalSupply', async () => {
- const ownerBalance = await zeroEx.token.getBalanceAsync(zrxAddress, owner);
+ const ownerBalance = await contractWrappers.token.getBalanceAsync(zrxAddress, owner);
const totalSupply = new BigNumber(await zrxToken.totalSupply.callAsync());
expect(totalSupply).to.be.bignumber.equal(ownerBalance);
});
@@ -79,11 +86,11 @@ describe('ZRXToken', () => {
describe('transfer', () => {
it('should transfer balance from sender to receiver', async () => {
const receiver = spender;
- const initOwnerBalance = await zeroEx.token.getBalanceAsync(zrxAddress, owner);
+ const initOwnerBalance = await contractWrappers.token.getBalanceAsync(zrxAddress, owner);
const amountToTransfer = new BigNumber(1);
- await zeroEx.token.transferAsync(zrxAddress, owner, receiver, amountToTransfer);
- const finalOwnerBalance = await zeroEx.token.getBalanceAsync(zrxAddress, owner);
- const finalReceiverBalance = await zeroEx.token.getBalanceAsync(zrxAddress, receiver);
+ await contractWrappers.token.transferAsync(zrxAddress, owner, receiver, amountToTransfer);
+ const finalOwnerBalance = await contractWrappers.token.getBalanceAsync(zrxAddress, owner);
+ const finalReceiverBalance = await contractWrappers.token.getBalanceAsync(zrxAddress, receiver);
const expectedFinalOwnerBalance = initOwnerBalance.minus(amountToTransfer);
const expectedFinalReceiverBalance = amountToTransfer;
@@ -101,9 +108,9 @@ describe('ZRXToken', () => {
describe('transferFrom', () => {
it('should return false if owner has insufficient balance', async () => {
- const ownerBalance = await zeroEx.token.getBalanceAsync(zrxAddress, owner);
+ const ownerBalance = await contractWrappers.token.getBalanceAsync(zrxAddress, owner);
const amountToTransfer = ownerBalance.plus(1);
- await zeroEx.token.setAllowanceAsync(zrxAddress, owner, spender, amountToTransfer, {
+ await contractWrappers.token.setAllowanceAsync(zrxAddress, owner, spender, amountToTransfer, {
gasLimit: constants.MAX_TOKEN_APPROVE_GAS,
});
const didReturnTrue = await zrxToken.transferFrom.callAsync(owner, spender, amountToTransfer, {
@@ -113,12 +120,12 @@ describe('ZRXToken', () => {
});
it('should return false if spender has insufficient allowance', async () => {
- const ownerBalance = await zeroEx.token.getBalanceAsync(zrxAddress, owner);
+ const ownerBalance = await contractWrappers.token.getBalanceAsync(zrxAddress, owner);
const amountToTransfer = ownerBalance;
- const spenderAllowance = await zeroEx.token.getAllowanceAsync(zrxAddress, owner, spender);
- const spenderAllowanceIsInsufficient = spenderAllowance.cmp(amountToTransfer) < 0;
- expect(spenderAllowanceIsInsufficient).to.be.true();
+ const spenderAllowance = await contractWrappers.token.getAllowanceAsync(zrxAddress, owner, spender);
+ const isSpenderAllowanceInsufficient = spenderAllowance.cmp(amountToTransfer) < 0;
+ expect(isSpenderAllowanceInsufficient).to.be.true();
const didReturnTrue = await zrxToken.transferFrom.callAsync(owner, spender, amountToTransfer, {
from: spender,
@@ -135,46 +142,46 @@ describe('ZRXToken', () => {
});
it('should not modify spender allowance if spender allowance is 2^256 - 1', async () => {
- const initOwnerBalance = await zeroEx.token.getBalanceAsync(zrxAddress, owner);
+ const initOwnerBalance = await contractWrappers.token.getBalanceAsync(zrxAddress, owner);
const amountToTransfer = initOwnerBalance;
const initSpenderAllowance = MAX_UINT;
- await zeroEx.token.setAllowanceAsync(zrxAddress, owner, spender, initSpenderAllowance, {
+ await contractWrappers.token.setAllowanceAsync(zrxAddress, owner, spender, initSpenderAllowance, {
gasLimit: constants.MAX_TOKEN_APPROVE_GAS,
});
- await zeroEx.token.transferFromAsync(zrxAddress, owner, spender, spender, amountToTransfer, {
+ await contractWrappers.token.transferFromAsync(zrxAddress, owner, spender, spender, amountToTransfer, {
gasLimit: constants.MAX_TOKEN_TRANSFERFROM_GAS,
});
- const newSpenderAllowance = await zeroEx.token.getAllowanceAsync(zrxAddress, owner, spender);
+ const newSpenderAllowance = await contractWrappers.token.getAllowanceAsync(zrxAddress, owner, spender);
expect(initSpenderAllowance).to.be.bignumber.equal(newSpenderAllowance);
});
it('should transfer the correct balances if spender has sufficient allowance', async () => {
- const initOwnerBalance = await zeroEx.token.getBalanceAsync(zrxAddress, owner);
- const initSpenderBalance = await zeroEx.token.getBalanceAsync(zrxAddress, spender);
+ const initOwnerBalance = await contractWrappers.token.getBalanceAsync(zrxAddress, owner);
+ const initSpenderBalance = await contractWrappers.token.getBalanceAsync(zrxAddress, spender);
const amountToTransfer = initOwnerBalance;
const initSpenderAllowance = initOwnerBalance;
- await zeroEx.token.setAllowanceAsync(zrxAddress, owner, spender, initSpenderAllowance);
- await zeroEx.token.transferFromAsync(zrxAddress, owner, spender, spender, amountToTransfer, {
+ await contractWrappers.token.setAllowanceAsync(zrxAddress, owner, spender, initSpenderAllowance);
+ await contractWrappers.token.transferFromAsync(zrxAddress, owner, spender, spender, amountToTransfer, {
gasLimit: constants.MAX_TOKEN_TRANSFERFROM_GAS,
});
- const newOwnerBalance = await zeroEx.token.getBalanceAsync(zrxAddress, owner);
- const newSpenderBalance = await zeroEx.token.getBalanceAsync(zrxAddress, spender);
+ const newOwnerBalance = await contractWrappers.token.getBalanceAsync(zrxAddress, owner);
+ const newSpenderBalance = await contractWrappers.token.getBalanceAsync(zrxAddress, spender);
expect(newOwnerBalance).to.be.bignumber.equal(0);
expect(newSpenderBalance).to.be.bignumber.equal(initSpenderBalance.plus(initOwnerBalance));
});
it('should modify allowance if spender has sufficient allowance less than 2^256 - 1', async () => {
- const initOwnerBalance = await zeroEx.token.getBalanceAsync(zrxAddress, owner);
+ const initOwnerBalance = await contractWrappers.token.getBalanceAsync(zrxAddress, owner);
const amountToTransfer = initOwnerBalance;
- await zeroEx.token.setAllowanceAsync(zrxAddress, owner, spender, amountToTransfer);
- await zeroEx.token.transferFromAsync(zrxAddress, owner, spender, spender, amountToTransfer, {
+ await contractWrappers.token.setAllowanceAsync(zrxAddress, owner, spender, amountToTransfer);
+ await contractWrappers.token.transferFromAsync(zrxAddress, owner, spender, spender, amountToTransfer, {
gasLimit: constants.MAX_TOKEN_TRANSFERFROM_GAS,
});
- const newSpenderAllowance = await zeroEx.token.getAllowanceAsync(zrxAddress, owner, spender);
+ const newSpenderAllowance = await contractWrappers.token.getAllowanceAsync(zrxAddress, owner, spender);
expect(newSpenderAllowance).to.be.bignumber.equal(0);
});
});
diff --git a/packages/contracts/tslint.json b/packages/contracts/tslint.json
index ffaefe83a..1ab924e47 100644
--- a/packages/contracts/tslint.json
+++ b/packages/contracts/tslint.json
@@ -1,3 +1,6 @@
{
- "extends": ["@0xproject/tslint-config"]
+ "extends": ["@0xproject/tslint-config"],
+ "rules": {
+ "custom-no-magic-numbers": false
+ }
}